# -*- coding: utf-8 # Copyright (c) 2008, HTK import simplejson from OFS.Folder import Folder from Globals import Persistent, InitializeClass from AccessControl import ClassSecurityInfo, getSecurityManager from zope.interface import implements from zope.component import getUtility, queryUtility from zope.component import ComponentLookupError from zope.sendmail.interfaces import IMailer from email.MIMEText import MIMEText from email.Header import Header from email.Utils import parseaddr, formataddr from interfaces import iMDPortal, IWaramu, IUsers, IContent, IBrowser, IVocabulary, ITranslator import cElementTree as ET from cStringIO import StringIO from Exceptions import AllMultilingualFieldsEmpty from config import perm_add_content, perm_manage from utils import getLanguage, translate from TempFileManager import TempFileManager class MDPortal(Persistent, Folder): implements(iMDPortal) security = ClassSecurityInfo() def __init__(self, *data, **kw): """ init """ super(MDPortal, self).__init__(data) def _setupPAS(self): """ Setup Pluggable Auth Service """ from zExceptions import BadRequest from Products.PluggableAuthService.PluggableAuthService import addPluggableAuthService # PAS try: addPluggableAuthService(self) except BadRequest: pass #cookie auth from Products.PluggableAuthService.plugins.CookieAuthHelper import addCookieAuthHelper try: addCookieAuthHelper(self.acl_users, 'cookie', '', '__ac') except BadRequest: pass self.acl_users.cookie.manage_activateInterfaces(['IExtractionPlugin', 'IChallengePlugin', 'ICredentialsUpdatePlugin', 'ICredentialsResetPlugin']) self.acl_users.cookie.login_path = '../../login.html' # acl_users/ZODBUser source from Products.PluggableAuthService.plugins.ZODBUserManager import addZODBUserManager try: addZODBUserManager(self.acl_users, 'users', '') except BadRequest: pass self.acl_users.users.manage_activateInterfaces(['IAuthenticationPlugin', 'IUserEnumerationPlugin', 'IUserAdderPlugin']) # roles from Products.PluggableAuthService.plugins.ZODBRoleManager import addZODBRoleManager try: addZODBRoleManager(self.acl_users, 'roles', '') except BadRequest: pass self.acl_users.roles.manage_activateInterfaces(['IRolesPlugin', 'IRoleEnumerationPlugin', 'IRoleAssignerPlugin']) # groups from Products.PluggableAuthService.plugins.ZODBGroupManager import addZODBGroupManager try: addZODBGroupManager(self.acl_users, 'groups', '') except BadRequest: pass self.acl_users.groups.manage_activateInterfaces(['IGroupsPlugin', 'IGroupEnumerationPlugin']) # create groups self.acl_users.groups.manage_addGroup('Admins', '', '') self.manage_setLocalRoles('Admins', ('MDManager',)) def _setupTempFolder(self): self.manage_addProduct['TemporaryFolder'].constructTemporaryFolder('cache', 'Cached content') def getTemp(self): return getattr(self, 'cache') def getTempFilestore(self): t = self.getTemp() if not hasattr(t, TempFileManager.id): tm = TempFileManager() t._setObject(tm.id, tm) return getattr(t, TempFileManager.id) def _setupPermissions(self): for r in ('MDManager', 'Creator'): self._addRole(r) from config import perm_view, perm_add_content, perm_manage self.manage_permission(perm_view, ('Anonymous',), 1) self.manage_permission(perm_add_content, ('Manager', 'MDManager', 'Creator', 'Authenticated'), 0) self.manage_permission(perm_manage, ('Manager', 'MDManager'), 0) def getPortalURL(self): return self.absolute_url() def _getMailer(self): try: mailer = getUtility(IMailer, 'mdportal.smtp') return mailer except ComponentLookupError: return None def register_handler(self, REQUEST): """ handler user's registration """ uname = REQUEST.get('username') fname = REQUEST.get('firstname') lname = REQUEST.get('lastname') email = REQUEST.get('email') usmsg = REQUEST.get('message') errno = 0 mailer = None ms = "" next = "mdportal.html" mailer = self._getMailer() if mailer is None: ms = 'reg_not_functional' errno = 4 if errno == 0 and (not uname or not fname or not lname or not email or not usmsg): ms = 'You must fill in all fields.' next = "register.html" errno = 2 # all ok if errno == 0: exu = self.acl_users.getUserById(uname) if exu is not None: ms = 'Username by that username already exists.' next = "register.html" errno = 3 al = self.restrictedTraverse('alert.html') if errno == 0: from utils import _genPwd pwd = _genPwd() self.acl_users._doAddUser(uname, pwd, [], []) ms = 'Your account is ready and password is emailed to you.' al.setColor('green') from User import User u = User(uname) u.setFirstName(fname) u.setLastName(lname) u.setEmail(email) self.users._setObject(uname, u) u = getattr(self.users, uname) uacl = self.acl_users.getUser(uname) u.manage_setLocalRoles(uname, ('Owner',)) u.changeOwnership(uacl) emsg = u"" emsg += translate(self, 'successful_account_creation', getLanguage(REQUEST)) emsg += '\n\n' emsg += translate(self, 'Server location:', getLanguage(REQUEST)) emsg += self.absolute_url() emsg += '\n' emsg += translate(self, 'Username:', getLanguage(REQUEST))+uname emsg += '\n' emsg += translate(self, 'Password:', getLanguage(REQUEST))+pwd self._send_email(fname+" "+lname, email, emsg) al.setMessage(translate(self, ms, getLanguage(REQUEST))) al.setNext(next) return al() def _send_email(self, _fname, _email, message): """ send password via email """ #TODO: translate mailer = self._getMailer() charset = 'utf-8' msg = MIMEText(message.encode('utf-8'), 'plain', charset) msg['From'] = formataddr(('', 'mdportal-dont-reply@htk.tlu.ee')) msg['To'] = formataddr((_fname, _email)) subject = 'Metadata Portal registration' msg['Subject'] = Header(subject, charset) mailer.send('mdportal-dont-reply@htk.tlu.ee', _email, msg.as_string()) def recover_password(self, REQUEST, recoverButton='', username='', email=''): """ recover password - new password will be generated and emailed """ al = self.restrictedTraverse('alert.html') msg = "" next = "recover.html" errno = 0 mailer = self._getMailer() if mailer is None: msg = 'reg_not_functional' errno = 4 if not errno and (not recoverButton or not email or not username): msg = 'valid_email_required' errno = 1 if not errno: um = getUtility(IUsers) user = um.getUserByUsername(username) if user is None: msg = 'user_by_username_not_found' errno = 2 if not errno and user.getEmail() != email: msg = 'user_by_email_not_found' errno = 2 uuser = self.acl_users.searchUsers(id=username) if len(uuser) != 1: msg = 'user_by_username_not_found' errno = 2 if not errno: from utils import _genPwd pwd = _genPwd() uuser = uuser[0] um = getattr(self.acl_users, uuser.get('pluginid')) um.updateUserPassword(username, pwd) emsg = translate(self, 'new_password_email', getLanguage(REQUEST)) emsg += '\n\n' emsg += translate(self, 'Server location:', getLanguage(REQUEST)) emsg += self.absolute_url() emsg += '\n' emsg += translate(self, 'Username:', getLanguage(REQUEST))+username emsg += '\n' emsg += translate(self, 'Password:', getLanguage(REQUEST))+pwd self._send_email(user.getFullName(), user.getEmail(), emsg) al.setColor('green') next = 'login.html' msg = 'new_password_emailed' al.setMessage(translate(self, msg, getLanguage(REQUEST))) al.setNext(next) return al() security.declareProtected(perm_manage, 'manage_tuneWaramu') def manage_tuneWaramu(self, REQUEST, waramu_location='', waramu_username='', waramu_password='', waramuTuneButton='', closeButton='', openConnectionButton=''): """ tune waramu settings """ if waramuTuneButton: w = getUtility(IWaramu, context=self).__of__(self) w._saveSettings(waramu_location, waramu_username, waramu_password) w._connect() if closeButton: w = getUtility(IWaramu, context=self).__of__(self) w._disconnect() if openConnectionButton: w = getUtility(IWaramu, context=self).__of__(self) w._connect() return REQUEST.RESPONSE.redirect('manage.html') security.declareProtected(perm_manage, 'manage_tuneGroups') def manage_tuneGroups(self, REQUEST): """ tune groups """ req = REQUEST if req.get('addNewGroup'): grid = req.get('newgroupid') grti = req.get('newgrouptitle') self.acl_users.groups.manage_addGroup(grid, grti) elif req.get('add_users_to_group'): groups = {} for x in req.form.keys(): if x.startswith('join_'): grid = req.get(x) unam = x[x.index('_')+1:] if not grid: continue if not groups.has_key(grid): groups[grid] = [] groups[grid].append(unam) for gr, mems in groups.items(): self.acl_users.groups.manage_addPrincipalsToGroup(gr, mems) return REQUEST.RESPONSE.redirect('manage.html') security.declareProtected(perm_manage, 'manage_setBrowsableFields') def manage_setBrowsableFields(self, REQUEST, fields): """ set browsable fields """ self._browsable_fields = fields return REQUEST.RESPONSE.redirect('manage.html') def listGroups(self): return self.acl_users.groups.listGroupInfo() def getGroupMembers(self, grid): return self.acl_users.groups.listAssignedPrincipals(grid) def listUsers(self): return self.acl_users.searchUsers() def getGroupsForUser(self, uid): u = self.acl_users.getUserById(uid) return u.getGroups() security.declareProtected(perm_add_content, 'CreateNew') def CreateNew(self, REQUEST, chooseButton='', typeID=''): """ preparing to create a new resource """ #REQUEST.SESSION.set('typeID', int(typeID)) o = self.content.restrictedTraverse('edit') o.setTypeID(typeID) return o() def searchHandler(self, searchButton, searchValue): """ search handler """ if not searchButton: # TODO: redirect to some nasty error page raise 'oops' if not searchValue: return self.REQUEST.RESPONSE.redirect('advanced_search.html') svals = searchValue.split(' ') sval = '/_fulltext/'.join(svals) return self.REQUEST.RESPONSE.redirect('browse/by/_fulltext/'+sval) def _collectValues(self, req, tid, tDesc): # collect values langs = req.get('md_rlang') root = ET.Element('data', xmlns=tid) #xml = u'\n' filled_langs = [] hasMlFields = False for x in tDesc.keys(): f = tDesc[x] fname = f.get('name') if f.get('multiLingual') == '1': hasMlFields = True for l in langs: fval = req.get(l+'_'+fname) if fval is None or not fval.strip(): continue if l not in filled_langs: filled_langs.append(l) if hasMlFields and not filled_langs: raise AllMultilingualFieldsEmpty for x in tDesc.keys(): f = tDesc[x] fname = f.get('name') if f.get('multiLingual') == '1': # collect all langs values for l in filled_langs: fval = req.get(l+'_'+fname) if fval is None or not fval.strip(): fval = "" delem = ET.SubElement(root, fname, lang=l) if f.get('type') == 'MultiString' or f.get('type') == 'MultiInteger': fvals = fval.split(',') for v in fvals: dval = ET.SubElement(delem, 'value') dval.text = v.decode('utf-8').strip() else: delem.text = fval.decode('utf-8').strip() else: fval = req.get(fname) if fval is None: fval = '' ns = '' if fname == 'curriculumSubject': ns = '{http://koolielu.ee/curriculum/09}' delem = ET.SubElement(root, ns+fname) if f.get('type') == 'MultiString' or f.get('type') == 'MultiInteger': fvals = fval.split(',') for v in fvals: v = v.strip() if not v: continue dval = ET.SubElement(delem, ns+'value') dval.text = v.decode('utf-8').strip() else: delem.text = fval.decode('utf-8').strip() stream = StringIO() tree = ET.ElementTree(root) tree.write(stream) stream.seek(0) xml = stream.read() return xml security.declareProtected(perm_add_content, 'uploadFile') def uploadFile(self, REQUEST): """ upload a file """ msg = 'Upload successful. Upload another file or click close.' tm = self.getTempFilestore() tm.addFile(REQUEST) return self.restrictedTraverse('upload.html')(msg = msg) security.declareProtected(perm_add_content, 'removeAttachmentJSON') def removeAttachmentJSON(self, rid, fid): """ remove a temporary file """ isTemp = True fid = fid[fid.index('_')+1:] if rid == 'None': # absolutely temp file pass else: if fid.isdigit(): # it's a digit so it must be from Waramu isTemp = False else: isTemp = True if isTemp: # remove from temp files uname = str(getSecurityManager().getUser()) tm = self.getTempFilestore() tm.deleteFile(fid, uname) else: w = getUtility(IWaramu, context=self).__of__(self) delOp = w.deleteAttachment(rid, fid) return self.listAttachmentsJSON(rid) security.declareProtected(perm_add_content, 'listAttachmentsJSON') def listAttachmentsJSON(self, rid): """ list attachments """ res = [] uname = str(getSecurityManager().getUser()) tm = self.getTempFilestore() if rid != 'None': w = getUtility(IWaramu, context=self).__of__(self) eats = w.listAttachments(rid) for aid, fname in eats.items(): res.append([str(aid), fname]) tmfiles = tm.getFilesByUser(uname) for tf in tmfiles: res.append([tf.getId(), tf.getFilename(), True]) return simplejson.dumps(res) security.declareProtected(perm_add_content, 'resourceHandler') def resourceHandler(self, REQUEST): """ resource handler. new or update object """ #TODO:XXX: refactor me req = REQUEST w = getUtility(IWaramu, context=self).__of__(self) tid = req.get('typeID') tDesc = w.getTypeDescription(tid) tinfos = w.getTypeInfos(tid) xml = None try: xml = self._collectValues(REQUEST, tid, tDesc) except AllMultilingualFieldsEmpty: al = self.restrictedTraverse('alert.html') al.setMessage(translate(self, 'All multilingual fields were empty. Fields for atleast one language must be filled.', getLanguage(REQUEST))) return al() rid = req.get('resourceID') if req.get('createButton'): # new resource rid = w.newResource(xml) elif req.get('updateButton'): # update resource if rid is None: raise 'serious error' res = w.updateResource(rid, xml) if not res: #oki-doki pass elif req.get('deleteButton'): return self.restrictedTraverse('delete_confirmation.html')(rid=rid) if tinfos['attachable'] or 1==1: #XXX uname = str(getSecurityManager().getUser()) tm = self.getTempFilestore() files = tm.getFilesByUser(uname) ids = [] for f in files: # send attachment ids.append(f.id) w.addAttachment(rid, f.getData(), f.getFilename()) [ tm.deleteFile(fid, uname) for fid in ids ] #return xml + "\n", rid return REQUEST.RESPONSE.redirect('content/view/'+rid) security.declareProtected(perm_add_content, 'deleteDialogHandler') def deleteDialogHandler(self, REQUEST, rid=None, confirmButton='', cancelButton=''): """ delete dialog handler """ if cancelButton: return REQUEST.RESPONSE.redirect('content/view/'+rid) elif confirmButton: w = getUtility(IWaramu, context=self).__of__(self) resp = w.deleteResource(rid) return REQUEST.RESPONSE.redirect('browse') return REQUEST.RESPONSE.redirect('mdportal.html') def getVocabulary(self, field=None, par=None): """ get vocabulary for a field """ u = queryUtility(IVocabulary, field) res = {'expands': False, 'results': []} if field is None: return res if u is not None: # use local vocabulary res['expands'] = u.treeLike() li = None if par is not None: parent = int(par) li = u.getChildren(parent) else: li = u.getMain() r2 = [] for l in li: r2.append([None, l['id'], l['t']]) res['results'] = r2 else: w = getUtility(IWaramu, context=self).__of__(self) trlang = getLanguage(self.REQUEST) res = w.getCombinedVocabulary(field, lang=trlang) res = {'expands': False, 'results': res} return simplejson.dumps(res) def translateJSON(self, field, s): """ translate """ t = s u = queryUtility(ITranslator, field) if u is not None: t = u.translate(s) # language return simplejson.dumps({'field': field, 'source': s, 'outcome': t}) def switchLanguage(self, lang='et'): """ switch UI language """ self.REQUEST.RESPONSE.setCookie('mdlang', lang) ref = self.REQUEST.get('HTTP_REFERER') if not ref: ref = self.getPortalURL() return self.REQUEST.RESPONSE.redirect(ref) InitializeClass(MDPortal) def added(obj, event): from zope.component import queryMultiAdapter from zope.interface import Interface from Products.Five.component import enableSite enableSite(obj) components_view = queryMultiAdapter((obj, obj.REQUEST), Interface, 'components.html') components_view.makeSite() obj._setupPAS() obj._setupTempFolder() obj._setupPermissions() sm = obj.getSiteManager() from Waramu import Waramu w = Waramu() w.id = 'waramu' obj._setObject('waramu', w) sm.registerUtility(getattr(obj, 'waramu'), IWaramu) from Users import Users u = Users() obj._setObject('users', u) u = getattr(obj, 'users') u.manage_permission('Edit', ('Manager', 'MDManager', 'Owner'), 0) sm.registerUtility(u, IUsers) from Content import Content c = Content() obj._setObject('content', c) sm.registerUtility(getattr(obj, 'content'), IContent) from Browser import Browser b = Browser() obj._setObject(b.id, b) sm.registerUtility(getattr(obj, b.id), IBrowser) sm.registerUtility(obj, iMDPortal)