# -*- coding: utf-8
# $Id$
#
# Copyright 2001, 2002 by IVA Team and contributors
#
# This file is part of IVA.
#
# IVA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# IVA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IVA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""This is the main module for FLE, containing the FLE class and the Zope factory method for creating a FLE installation inside Zope."""
__version__ = "$Revision$"[11:-2]
from common import IVA_VERSION
import os.path, string, time
try:
from PIL import Image
PIL_imported = 1
except ImportError:
PIL_imported = 0
try:
from Products.ZWiki.ZWikiPage import ZWikiPage
ZWIKI_imported = 1
except ImportError:
ZWIKI_imported = 0
try:
from Products.ZMySQLDA.DA import Connection
SQL_INSTALLED = 1
except ImportError:
SQL_INSTALLED = 0
import Globals
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from Products.StandardCacheManagers import AcceleratedHTTPCacheManager
from Globals import Persistent, Acquisition, HTMLFile, PersistentMapping
import AccessControl
import OFS
from OFS.Folder import Folder
from OFS.interfaces import IFolder
from Products.ZCatalog.ZCatalog import ZCatalog
from TraversableWrapper import TraversableWrapper as TW
from Cruft import Cruft
from ThinkingTypeSetManager import ThinkingTypeSetManager as TTSM
from UserManager import UserManager
from CourseManager import CourseManager
from common import file_path, translate
import Acquisition
import YlTest
from string import atoi
from common import perm_view, perm_edit, perm_manage, perm_add_lo, perm_access, \
roles_admin, roles_staff, roles_user, _
import common
import re
from YlHoidla import YlHoidla
from interfaces import IFLE, ICourseManager
from zope.component import adapter
from zope.app.container.interfaces import IObjectAddedEvent
from zope.sendmail.interfaces import IMailer
try:
from Products.CMFCore.CookieCrumbler import CookieCrumbler, ATTEMPT_NONE, ATTEMPT_LOGIN, ATTEMPT_RESUME
CCAware = 1
except:
CCAware = 0
from OFS.OrderSupport import OrderSupport
from zope.interface import implements
from zope.component import getUtility, queryUtility
from Products.Five.component.interfaces import IObjectManagerSite
from zope.event import notify
from zope.app.publication.zopepublication import BeforeTraverseEvent
from OFS.Folder import manage_addFolder
from zope.app.component.interfaces import ISite, IPossibleSite
# This is the class for the FLE installation root object.
class FLE(
Folder,
TW,
Cruft,
Persistent,
AccessControl.Role.RoleManager,
OrderSupport,
):
"""FLE product."""
security = AccessControl.ClassSecurityInfo()
security.declareObjectPublic()
meta_type = 'IVA'
implements(IFLE, IPossibleSite, IFolder)
has_order_support = 0
manage_options=(
{'label' : 'Tools', 'action' : 'redirect_to_manage', 'filter': lambda x: not hasattr(x, 'Management')},
{'label' : 'Tools', 'action' : 'Management/management_tab', 'filter': lambda x: hasattr(x, 'Management')},
) + OFS.Folder.Folder.manage_options
standard_error_message = PageTemplateFile('browser/templates/main/standard_error_message.zpt', globals())
error_show_traceback = PageTemplateFile('browser/templates/main/error_show_traceback.zpt', globals())
def __before_publishing_traverse__(self, arg1, arg2=None):
pass
def redirect_to_manage(self, REQUEST=None):
""" asdasd """
from Management import Management
if not hasattr(self, Management.id):
self._setObject(Management.id, Management())
if REQUEST:
return REQUEST.RESPONSE.redirect('Management/management_tab')
return 0
# Parameters are received from the creation form at
# ui/FLE/creation_form.dtml
def __init__(
self, _id, title,
smtp_host, smtp_port=25,
_default_lang='en', _quota=3000000, mfrom='iva@htk.tlu.ee',
def_passwd=0,def_not_change=0,def_not_aff=0):
"""Construct FLE object."""
errors = []
if not _id:
errors.append('ID not specified')
if not title:
errors.append('Title not specified')
if errors:
raise 'Insufficient form data',str(errors)
self.id = _id
self._default_lang=_default_lang
self._quota=_quota
self.title = title
self.mfrom = mfrom
self.def_asswd= int(def_passwd)
self.def_ot_change= int(def_not_change)
self.def_ot_aff= int(def_not_aff)
self._pending_users = PersistentMapping() # user list
self.__fle_root = ''
# ZCatalog for webtop items
catalog = ZCatalog('catalog_webtop_items', 'ZCatalog for webtop items')
self._setObject('catalog_webtop_items', catalog)
catalog = self.catalog_webtop_items
# indexes
catalog.addIndex('getObjPermission', 'FieldIndex')
catalog.addIndex('getWeight', 'FieldIndex')
catalog.addIndex('get_name', 'FieldIndex')
catalog.addIndex('get_size', 'FieldIndex')
catalog.addIndex('get_timestamp', 'FieldIndex')
catalog.addIndex('meta_type', 'KeywordIndex')
#catalog.addIndex('get_content', 'TextIndex') # indexing binary data would be stupid
class largs:
def __init__(self, **kw):
self.__dict__.update(kw)
catalog.addIndex('get_author', 'FieldIndex')
catalog.addIndex('path', 'IVAPathIndexNG')
class DRattrs:
since_field='getStartTimeIndex'
until_field='getEndTimeIndex'
catalog.addIndex('visible', 'DateRangeIndex', DRattrs())
from Products.ZCTextIndex.ZCTextIndex import manage_addLexicon
manage_addLexicon(catalog, 'lexicon',
elements=[
largs(group= 'Case Normalizer' , name= 'Case Normalizer' ),
largs(group= 'Stop Words', name= " Don't remove stop words" ),
largs(group= 'Word Splitter' , name= "IVA Unicode Whitespace splitter" ),
]
)
class attrs:
doc_attr = 'get_name'
lexicon_id = 'lexicon'
index_type = 'Cosine Measure'
catalog.addIndex('fulltext', 'ZCTextIndex', attrs())
# metadata
catalog.addColumn('absolute_url')
catalog.addColumn('getRelativeContentURL')
catalog.addColumn('getTitle')
catalog.addColumn('getWeight')
catalog.addColumn('get_author')
catalog.addColumn('get_icon_path')
catalog.addColumn('get_id')
catalog.addColumn('get_name')
catalog.addColumn('get_size')
catalog.addColumn('get_timestamp')
catalog.addColumn('kasWikiKaust')
catalog.addColumn('lastModification')
catalog.addColumn('meta_type')
catalog.addColumn('getObjPermission')
#self._setObject('catalog_webtop_items', catalog)
# ZCatalog for events
catalog = ZCatalog('catalog_events','ZCatalog for events')
# indexes
catalog.addIndex('meta_type', 'FieldIndex')
catalog.addIndex('path', 'IVAPathIndexNG')
catalog.addIndex('kysiOlek', 'FieldIndex')
catalog.addIndex('getName', 'FieldIndex')
catalog.addIndex('asukohaNimi', 'FieldIndex')
catalog.addIndex('get_start_time', 'DateIndex')
catalog.addIndex('get_end_time', 'DateIndex')
# metadata
catalog.addColumn('getName')
catalog.addColumn('kysiOlek')
catalog.addColumn('getContent')
catalog.addColumn('kysiSiseviide')
catalog.addColumn('kysiViide')
catalog.addColumn('kysiAlgus')
catalog.addColumn('kysiLopp')
catalog.addColumn('get_start_time')
catalog.addColumn('get_end_time')
catalog.addColumn('absolute_url')
catalog.addColumn('meta_type')
# add object
self._setObject('catalog_events', catalog)
# blogs zcatalog
self.add_blogs_zcatalog()
security.declarePrivate('add_blogs_zcatalog')
def add_blogs_zcatalog(self):
""" add catalog for blog entries """
# ZCatalog for blogs
if hasattr(self, 'blogs_zcatalog'):
return
catalog = ZCatalog('blogs_zcatalog', 'ZCatalog for blogs')
class largs:
def __init__(self, **kw):
self.__dict__.update(kw)
from Products.ZCTextIndex.ZCTextIndex import manage_addLexicon
manage_addLexicon(catalog, 'lexicon',
elements=[
largs(group= 'Case Normalizer' , name= 'Case Normalizer' ),
largs(group= 'Stop Words', name= " Don't remove stop words" ),
largs(group= 'Word Splitter' , name= "IVA Unicode Whitespace splitter" ),
]
)
class attrs:
doc_attr = 'getTitle, getContent, getIntro'
lexicon_id = 'lexicon'
index_type = 'Cosine Measure'
catalog.addIndex('fulltext', 'ZCTextIndex', attrs())
#indexes
catalog.addIndex('meta_type', 'FieldIndex')
catalog.addIndex('getNumberOfComments', 'FieldIndex')
catalog.addIndex('getID', 'FieldIndex')
catalog.addIndex('getTitle', 'FieldIndex')
catalog.addIndex('getIntro', 'FieldIndex')
catalog.addIndex('getContent', 'FieldIndex')
catalog.addIndex('getAuthor', 'FieldIndex')
catalog.addIndex('getTime', 'FieldIndex')
catalog.addIndex('path', 'IVAPathIndexNG')
#metadata
catalog.addColumn('absolute_url')
catalog.addColumn('getNumberOfComments')
catalog.addColumn('getID')
catalog.addColumn('getTitle')
catalog.addColumn('getIntro')
catalog.addColumn('getContent')
catalog.addColumn('getAuthor')
catalog.addColumn('get_author')
catalog.addColumn('get_timestamp')
catalog.addColumn('getTime')
#add object
self._setObject('blogs_zcatalog', catalog)
# def manage_beforeDelete(self, item, container):
# """ remove yourself from scheduler """
# #rename stats files
# #XXX: remove sql tables
# import os
security.declareProtected(perm_manage, 'reload_typesets')
def reload_typesets(self,REQUEST=None):
"""Reload typesets."""
self.typesets.load_default_sets()
if REQUEST:
return self.restrictedTraverse('message_dialog.html')(
self, REQUEST,
title='Reloaded',
message='Knowledge type sets reloaded',
action=self.absolute_url())
security.declareProtected(perm_manage, 'reload_all')
def reload_all(self, REQUEST=None):
"""Reload all dtml, style sheet, image, printer, and
translation files."""
self.IVA_VERSION = IVA_VERSION
self.reload_typesets()
self.reload_images()
if REQUEST:
return self.restrictedTraverse('message_dialog.html')(
self, REQUEST,
title='Reloaded',
message='Reloaded all knowledge type sets',
action=self.absolute_url())
security.declareProtected(perm_manage, 'reload_images')
def reload_images(self, REQUEST=None):
""" relaod images. Only used for frontpage images """
import os
image_file_path = os.path.join(file_path, 'ui', 'images')
self.add_images(self.images, image_file_path)
getattr(self.images, 'frontpage').manage_permission(perm_view, ('Anonymous',) ,1)
if REQUEST:
return self.restrictedTraverse('message_dialog.html')(
title = 'Reloaded',
message = 'Images reloaded',
action = self.absolute_url())
def add_images(self, zope_path, file_path):
""" add image to images folder """
for e in os.listdir(file_path):
full_path = os.path.join(file_path, e)
if os.path.isdir(full_path):
if e == 'CVS': continue
if e == '.svn': continue
try:
manage_addFolder(zope_path, e, '')
except:
pass
self.add_images(getattr(zope_path, e), full_path)
else:
# title (3rd parameter) will be used as an alt parameter
# inside HTML tag. (Override in DTML code when you
# want some alt text (i.e. almost always...))
# This can be like this for now .... -granbo
import re
m = re.match("^(.*)\.", e)
f = open(full_path,"rb")
content = f.read()
f.close()
try:
zope_path.manage_addImage(m.group(1), content, '')
except:
zope_path._delObject(m.group(1))
zope_path.manage_addImage(m.group(1), content, '')
img = getattr(zope_path, m.group(1))
img.ZCacheable_setManagerId('HTTPCache')
security.declarePublic('get_version')
def get_version(self):
""" return IVA version """
return IVA_VERSION
def get_db_version(self):
""" return an instance's version """
return self.IVA_VERSION
# FIXME: What should we do if self.webtop_quota_amount is invalid?
security.declareProtected(perm_view, 'get_quota')
def get_quota(self):
"""Return quota limit in bytes (-1 if no quota in use)."""
if not hasattr(self, 'webtop_quota'):
return -1
if not hasattr(self, 'webtop_quota_amount'):
return -1
if not self.webtop_quota:
return -1 # no quota
s = self.webtop_quota_amount.strip()
if s[-1].lower() == 'k':
return int(s[:-1]) * 1024
elif s[-1].lower() == 'm':
return int(s[:-1]) * 1024 * 1024
else:
return int(s)
#XXX: security check!
# security.declareProtected(perm_view, 'get_languages')
security.declarePublic('get_languages')
def get_languages(self):
""" return list of languages available """
lang_list = ['et','en','fi','ru', 'de']
return lang_list
def get_default_language(self):
"Vaikimisi keel"
return getattr(self, 'default_language', 'en')
# This is used by message_dialog.dtml.
def message_dialog_handler(self, action, REQUEST):
"""Redirects to the given URL, preserving the
state information in the url."""
REQUEST.RESPONSE.redirect(action)
def _redirect_checks(self, REQUEST):
""" redirect checks """
uname = str(REQUEST.AUTHENTICATED_USER)
if (not hasattr(self.fle_users.aq_self, uname)):
result = self.fle_users.add_user_fle(uname, tuple())
user = self.fle_users.get_user_info(uname)
if self.getUseFinger():
user.fingerNames()
#jnr = user.get_jooksev_kursus()
cm = getUtility(ICourseManager)
cm = cm.__of__(self)
cm.change_course(REQUEST, '0')
return user
def fle_url(self):
""" return fle_url """
url = str(self.unrestrictedTraverse('@@absolute_url'))
return url
security.declareProtected(perm_view, 'redirect_to_webtop')
def redirect_to_webtop(self, REQUEST):
"""Redirect to webtop."""
user = self._redirect_checks(REQUEST)
uname = user.get_uname()
if user.get_first_name()=='' or user.get_last_name()=='':
return REQUEST.RESPONSE.redirect(self.fle_url()+'/fle_users/' + uname + '/edit_user_form.html')
if hasattr(user, 'perm_must_change'):
if user.perm_must_change == 1:
return REQUEST.RESPONSE.redirect(self.fle_url()+'/fle_users/' + uname + '/edit_user_form.html')
else:
return REQUEST.RESPONSE.redirect(self.fle_url()+'/organizer_index.html')
else:
return REQUEST.RESPONSE.redirect(self.fle_url()+'/organizer_index.html')
security.declareProtected(perm_view, 'redirect_to_management')
def redirect_to_management(self, REQUEST):
""" redirect to management """
user = self._redirect_checks(REQUEST)
return REQUEST.RESPONSE.redirect(self.fle_url() + '/manage_iva_setup.html')
security.declareProtected(perm_manage, 'manage_workspace')
def manage_workspace(self,REQUEST):
"""This makes sure that ZCatalog doesn't override manage_workspace
and make it point to index_html, when we actually want manage_main"""
REQUEST.RESPONSE.redirect('manage_main')
def logout(self,REQUEST):
"""This is the logout instruction page."""
return self.restrictedTraverse('message_dialog2.html')(
self, REQUEST,
title= 'Logout',
message="To log out, either close your browser or press the Logout button below. When your browser asks for a username and password, just press Cancel. This will clear your login information from your browser's cache",
extra_value_name = '',
extra_values = (),
option1_value = 'Cancel',
option1_name = 'cancel',
option2_value = 'Logout',
option2_name = 'logout',
handler = 'clear_password')
security.declarePublic('clear_password')
def clear_password(
self,
cancel=None,
logout=None,
REQUEST=None):
"""This will clear the password cache."""
if logout:
#XXX: take care of session somehow, invalidating doesn't seem to work
#REQUEST.SESSION.invalidate()
#REQUEST.RESPONSE.setStatus(401)
#REQUEST.RESPONSE.setHeader("WWW-Authenticate", 'basic realm="Zope"')
try: del REQUEST['HTTP_REFERER']
except AttributeError: pass
REQUEST.RESPONSE.expireCookie('__ac', path='/')
REQUEST.RESPONSE.expireCookie('_ZopeId', path='/')
return self.restrictedTraverse('standalone_dialog.html')(
self, REQUEST,
title='Logout',
message='You have been logged out.',
action = self.absolute_url())
else:
return self.redirect_to_webtop(REQUEST)
def has_PIL(self):
"""Has the system PIL (Python Image Library) installed?"""
return PIL_imported
def has_MySQL(self):
return SQL_INSTALLED
def getId(self):
""" Return object id. Hack XXX """
return self.id
security.declarePublic('register_form_handler')
def register_form_handler(self, REQUEST,
regB='', cancelB='', # buttons
username='', fname='', lname='', email='', preflang='en',optmess='', numbercheck=0):
""" registers user """
if regB and self.get_allow_autoregister():
ok = 1
inv_val = ''
if username and fname and lname and email:
ok = 1
if not '@' in email and not '.' in email:
ok = 0
inv_val+= ' email'
else:
#XXX check email!?
pass
if not fname:
ok = 0
if inv_val: inv_val+=','
inv_val+=' first name'
if not lname:
ok = 0
if inv_val: inv_val+=','
inv_val+=' last name'
if numbercheck != REQUEST.SESSION.get('securenr'):
ok = 0
if inv_val: inv_val+=','
inv_val+= ' number'
if not username:
ok = 0
if inv_val: inv_val+=','
inv_val+=' username'
else:
if self.fle_users.is_valid_uname(username):
ok = 0
if inv_val: inv_val+=','
inv_val+=' username'
# XXX: maybe we don't want that!
# XXX: also check in fle_users folder for user record
# XXX: check for lowlevel acl_users?
if not ok:
return self.restrictedTraverse('register_form.html')(username=username, fname=fname, lname=lname, email=email, optmess=optmess,mess='invalid values: '+inv_val)
#do queueing
self._queue_user(username=username, fname=fname, lname=lname, email=email, preflang=preflang,optmess=optmess)
#XXX: subject of email. Translation!
message = translate(self,"registrant_waiting", target=preflang)
if self.saadaEmail(email,'useraccount request', message):
return translate(self,"You have been registered, check e-mail")
else:
return translate(self,"You have been registered but some problems occured when sending email.")
elif cancelB:
return REQUEST.RESPONSE.redirect(self.absolute_url())
raise "IVA Error", "unknown button in register_form_handler"
def lostPassword(self,REQUEST, send=0,knimi='', give_email=0):
if give_email:
res = 1
if not knimi: res = 0
kasutaja = None
try:
kasutaja = self.fle_users.get_user_info(knimi)
except KeyError:
res = 0
except AttributeError:
res = 0
if kasutaja is None:
return 0
if kasutaja.get_email() != '':
res = 1
else:
res = 0
if self.def_not_change:
res = 0
if kasutaja.getSetting('perm_pass'):
res = 0
return res
mailer = queryUtility(IMailer, 'iva.smtp')
if mailer is None:
return ""
if send:
try:
kasutaja = self.fle_users.get_user_info(knimi)
except KeyError:
return 0
except AttributeError:
return 0
if kasutaja.get_email() == '':
return 0
sisu = '' #\nContent-Type:text/plain; charset=utf-8\n\n\n'
sisu += "\n"+translate(self,'Message from IVA server')
sisu += "\n"+translate(self,'IVA server address:')+" "+self.fle_url()
sisu += "\n"+translate(self,'Username:')+" "+knimi
sisu += "\n"+translate(self,'Password:')+" "
new_pass = self.fle_users.looParool()
try:
kasutaja.set_password(new_pass)
sisu += new_pass
except:
sisu += translate(self,"Error fetching password")
self.saadaEmail(kasutaja.get_email(), translate(self, 'Lost password'), sisu, kasutaja.get_email())
return "Recover password"
security.declareProtected(perm_manage,'manage_iva_setup_pictures')
def manage_iva_setup_pictures(self, REQUEST, uploadB='', deleteB='',dele='', newimage=''):
""" upload picture, delete picture """
imgContainer = getattr(self.images, 'frontpage')
if deleteB:
from types import StringType
if type(dele) == StringType: dele = (dele,)
for x in dele:
imgContainer._delObject(x)
if uploadB and newimage:
import random
nid = ""
for x in range(15):
nr = 95
while 90