def block_portlets(self, action): status = IStatusMessage(self.request) context = Acquisition.aq_inner(self.context) data, error = self.extractData() portlet_manager = data.get('portlet_manager', None) blockstatus = data.get('blockstatus', False) manager = data.get('portlet_manager', None) path = "/".join(context.getPhysicalPath()) if manager is not None: managernames = [manager] else: managernames = utils.get_portlet_manager_names() status.addStatusMessage(u'Set portlet block status on %s' % ', '.join( managernames), type='info') managers = dict() for managername in managernames: managers[managername] = assignment_mapping_from_key( context, managername, CONTEXT_CATEGORY, path, ) info, warnings, errors = utils.exec_for_all_langs( context, utils.block_portlets, managers=managers, blockstatus=blockstatus, ) self.handle_status(status, info, warnings, errors)
def __call__(self): """ """ request = aq_inner(self.request) form = request.form if not 'form.button.Delete' in form: return super(PatchedManageAssignments, self).__call__() context = aq_inner(self.context) assignable = IRuleAssignmentManager(context) storage = getUtility(IRuleStorage) status = IStatusMessage(self.request) rule_ids = form.get('rule_ids', ()) path = '/'.join(context.getPhysicalPath()) for r in rule_ids: del assignable[r] assignments = get_assignments(storage[r]) if path in assignments: msg = 'Try to remove from %r' % ( assignments ) logger.info(msg) try: assignments.remove(path, None) except: assignments.remove(path) status.addStatusMessage(_(u"Assignments deleted."), type='info') return self.template()
def handle_save(self, action): data, errors = self.extractData() if errors: return props = dict( title=data['title'], description=data.get('description', ''), remoteUrl=data['remoteUrl'], fileCategories=tuple( tuple(x.values()) for x in data['fileCategories'] ) ) lfa = LandFileApi(self.context.landfiles) try: landfile = lfa.add_with_filesize(**props) except (KeyError, OSError) as err: raise ActionExecutionError(Invalid(err.message)) messages = IStatusMessage(self.request) messages.add(u'Added landfile: {}'.format(landfile.title)) self.redirect_to_listing()
def __call__(self): manager = self.context.restrictedTraverse(VIEW_NAME) if manager.isin(): manager.rm() status = IStatusMessage(self.request) status.add(_(u"Removed from favorite")) self.request.response.redirect(self.nextURL())
def __call__(self): context = aq_inner(self.context) request = self.request response_id = self.validate_response_id() file = None if response_id != -1: response = self.folder[response_id] file = response.attachment if file is None: status = IStatusMessage(request) msg = _(u"Response id ${response_id} has no attachment.", mapping=dict(response_id=response_id)) msg = translate(msg, 'Poi', context=context) status.addStatusMessage(msg, type='error') if file is None: request.response.redirect(context.absolute_url()) # From now on file exists. # Code mostly taken from Archetypes/Field.py:FileField.download filename = getattr(file, 'filename', file.getId()) if filename is not None: if FILE_NORMALIZER: filename = IUserPreferredFileNameNormalizer(request).normalize( safe_unicode(filename, context.getCharset())) else: filename = safe_unicode(filename, context.getCharset()) header_value = contentDispositionHeader( disposition='attachment', filename=filename) request.response.setHeader("Content-disposition", header_value) return file.index_html(request, request.response)
def send_unsubscribe_email(self, subscriber): newsletter = self.context catalog = getToolByName(self.context, "portal_catalog") query = {} query["portal_type"] = "ENLSubscriber" query["email"] = subscriber results = catalog.unrestrictedSearchResults(query) messages = IStatusMessage(self.request) if results: subscriber_brain = results[0] unsubscribe_url = self.newsletter_url +\ '/unsubscribe?subscriber=' + subscriber_brain.UID msg_text = """%s: %s""" % ( newsletter.getUnsubscribe_string(), unsubscribe_url) api.portal.send_email( recipient=subscriber, sender=self.context.email_from_address, subject=_(u"confirm newsletter unsubscription"), body=msg_text, ) messages.addStatusMessage( _("We send you an email, please confirm this unsubscription."), "info") else: # todo: write an extra error msg if a plone user wants to # unsubscribe himself messages.addStatusMessage( _("Your email address could not be found in subscribers."), "error")
def sendMail( self, msg, mail_from, mail_to, subject, ): if mail_to: bcc_to = [] if NOTIFICATION_LOG_ADDR: msg = u'''From: %s To: %s Bcc: %s Subject: %s ''' \ % (mail_from, mail_to, NOTIFICATION_LOG_ADDR, subject) + msg bcc_to = bcc_to + [NOTIFICATION_LOG_ADDR] if not isinstance(mail_to, list): mail_to = [mail_to] self.mailhost.send(messageText=msg, mto=mail_to + bcc_to, mfrom=mail_from, subject=subject, charset='utf-8') else: (mail_to, pref_lang) = self.findRecipient() messages = IStatusMessage(self.request) messages.addStatusMessage(self.ts.translate(_('mail_no_recipients' ), target_language=pref_lang), type='warning')
def handleApply(self, action): data, errors = self.extractData() messages = IStatusMessage(self.request) if errors: return False try: source = "*****@*****.**" recipients = [recipients_voc.getTerm(data["recipient"]).value, ] subject = "[Sito Viola] Nuova mail da %(name)s" % data # Custom message with a name filled in message = u"""Hai ricevuto una mail di contatto dal sito violareggiocalabria.it: Dati del richiedente: Nome: %(name)s Email: %(email)s Telefono: %(phone)s Testo: %(text)s """ % (data) encoding = 'utf-8' message = MIMEText(message, 'plain', encoding) message['Reply-To'] = Header(data.get("email", source), encoding) send_mail(source, recipients, subject, message) messages.add(u"""La tua email è stata inviata con successo. Ti risponderemo prima possibile.""", type=u"info") except: messages.add(u"""C'è stato un problema nell'invio della posta. Riprova o contattaci telefonicamente.""", type=u"error") return False
def handle_migrate(self, action): data, errors = self.extractData() if errors: return changed_base_classes = data.get('changed_base_classes', []) if not changed_base_classes: return migrate_to_folderish = data.get('changed_base_classes', False) catalog = getToolByName(self.context, "portal_catalog") migrated = [] not_migrated = [] for brain in catalog(): obj = brain.getObject() old_class_name = dxmigration.get_old_class_name_string(obj) if old_class_name in changed_base_classes: if dxmigration.migrate_base_class_to_new_class( obj, migrate_to_folderish=migrate_to_folderish): migrated.append(obj) else: not_migrated.append(obj) messages = IStatusMessage(self.request) info_message_template = 'There are {0} objects migrated.' warn_message_template = 'There are not {0} objects migrated.' if migrated: msg = info_message_template.format(len(migrated)) messages.addStatusMessage(msg, type='info') if not_migrated: msg = warn_message_template.format(len(not_migrated)) messages.addStatusMessage(msg, type='warn') self.request.response.redirect(self.request['ACTUAL_URL'])
def update(self): super(FoundationDashboard, self).update() if self.empty() and self.can_edit(): status = IStatusMessage(self.request) msg = _p(u"info_empty_dashboard") msg = translate(msg, domain='plone', context=self.request) status.add(msg, "info")
def plonegroup_contact_transition(contact, event): """ React when a IPloneGroupContact transition is done """ if event.transition and event.transition.id == 'deactivate': # check if the transition is selected registry = getUtility(IRegistry) pp = api.portal.get_tool('portal_properties') errors = [] if contact.UID() in registry[ORGANIZATIONS_REGISTRY]: errors.append(_('This contact is selected in configuration')) elif pp.site_properties.enable_link_integrity_checks: search_value_in_objects(contact, contact.UID(), p_types=[], type_fields={}) storage = ILinkIntegrityInfo(contact.REQUEST) breaches = storage.getIntegrityBreaches() if contact in breaches: errors.append(_("This contact is used in following content: ${items}", mapping={'items': ', '.join(['<a href="%s" target="_blank">%s</a>' % (i.absolute_url(), i.Title()) for i in breaches[contact]])})) if errors: smi = IStatusMessage(contact.REQUEST) smi.addStatusMessage(_('You cannot deactivate this item !'), type='error') smi.addStatusMessage(errors[0], type='error') view_url = getMultiAdapter((contact, contact.REQUEST), name=u'plone_context_state').view_url() # contact.REQUEST['RESPONSE'].redirect(view_url) raise Redirect(view_url)
def __call__(self): # check that we can actually access this form, # aka the current user has an advice to add or edit form = self.request.form cancelled = form.get('form.button.Cancel', False) submitted = form.get('form.button.Migrate', False) # test = form.get('form.button.Test', False) if submitted: # proceed, call the migration methdd results = self.migrate() messages = IStatusMessage(self.request) for migration_result in results: res_type = migration_result.get('type') res_infos = migration_result.get('infos') if res_infos.get('errors'): messages.add( u'Error when migrating "%s" type. Check the ' u'log for other informations.' % res_type, type=u"error") else: msg = translate( 'Migration applied succesfully for %s "%s" items.' % (res_infos.get('counter'), res_type), domain='plone.app.contenttypes') messages.add(msg, type=u"info") elif cancelled: self.request.response.redirect(form.get('form.HTTP_REFERER')) return self.index()
def _on_save(self, data): """ Deleting the gray-scale converted resources from the filesystem cache """ messages = IStatusMessage(self.request) if data.get("delete_cached_resources"): site = getSite() try: path = str("/++%s++%s" % (TYPE, THEME)) location = site.restrictedTraverse(path).directory except AttributeError, e: log.info("Unable to remove files for: %s due to error: %s" % (path, e)) message = _(u"Couldn't remove files from %s" % path) messages.addStatusMessage(message, type="error") return try: log.info("Removing cached gray-scale files from: %s" % location) for filename in os.listdir(location): file_path = os.path.join(location, filename) if os.path.isfile(file_path): os.unlink(file_path) else: shutil.rmtree(file_path) except OSError, e: log.info("Unable to remove files from: %s, error: %s" % (location, e)) message = _(u"Couldn't remove files from %s" % location) messages.addStatusMessage(message, type="error")
def render(self, *a, **kw): errors = [] results = '' status = IStatusMessage(self.request) data = self.request['_data'] # In the future we might implement other ways to render file # information, either for other types of backends or even file # formats (which file renderer should take over, but...), so # this serves as a placeholder of sort. view = 'fileinfo' fileview = zope.component.getMultiAdapter( (self.context, self.request), name=view) try: # XXX current way to disable main_template results = fileview.call_template() except: errors.append('failed to render file information') if errors: # probably should only show this info message to users who # can deal with this. status.addStatusMessage(' '.join(errors), 'info') if results: return results
def set_blacklist_status(self, manager, group_status, content_type_status, context_status): """Stay on the same page rather then redirecting to the portlet management view. """ superclass = super(CustomManageContextualPortlets, self) superclass.set_blacklist_status( manager, group_status, content_type_status, context_status ) # Give some feedback to the user, otherwise he might miss the fact # that the form has been submitted. messages = IStatusMessage(self.request) messages.add(text=_(u'Settings have been saved.'), type=u'success') base_url = str( getMultiAdapter((self.context, self.request), name='absolute_url') ) if self.request.get('HTTP_REFERER', '').endswith('manage-footer'): # This is the part we need to override from the parent class. self.request.response.redirect(base_url + '/manage-footer') else: self.request.response.redirect(base_url + '/@@manage-portlets') return ''
def handleCancel(self, action): messages = IStatusMessage(self.request) messages.add(_(u"Review submission canceled"), type=u"info") redirect = self.request.response.redirect redirect(self.context.absolute_url())
def handle_buscar(self, action, data): db = getUtility(IDatabase, name=DB_NAME) mapper_class = db.mappers['posto'].class_ query = db.session.query(mapper_class) ativo = data.get('ativo') query = query.filter(mapper_class.ativo == ativo) #only add these items in a query if value is True for item in ['ferramentas','eletroportateis','eletronicos','bebedouros']: value = data.get(item, False) if value: query = query.filter(getattr(mapper_class, item) == value) modificado = data.get('modificado') criacao = data.get('criacao') if modificado is not None: query = query.filter(mapper_class.modificado >= modificado) if criacao is not None: query = query.filter(mapper_class.criacao >= criacao) for field in ['cod','cidade','bairro','uf']: if data.get(field) is not None: value = data.get(field) query = query.filter("%s ~* '%s'" % (field, value)) count = query.count() self.results = query.all() if not self.results: msg = "Nenhum registro foi encontrado." else: msg = "%d registro(s)." % count status = IStatusMessage(self.request) status.addStatusMessage(msg, u'info')
def __call__(self, request): value = request.form.get(self.field_name, request.get(self.field_name, None)) if value is not None: messages = IStatusMessage(request) catalog = getToolByName(self.context, "portal_catalog") plone_utils = getToolByName(self.context, 'plone_utils', None) parent = aq_parent(aq_inner(self.context)) # Get the object metatype that we are going to create object_type = self.context.Type() # Actual folder where the object is going to be created, ignoring the portal_factory check object_folder = str('/'.join(parent.getPhysicalPath())) object_folder = object_folder.replace('/portal_factory/'+object_type,'') results = self.context.portal_catalog.searchResults(portal_type=object_type, path={'query':object_folder,'level':0,'depth':1}, Title=value) for result in results: if self.context.UID() == result.getObject().UID(): return None if results: messages.addStatusMessage(_(u'There is an object in this folder with the same type and title'), type="error") return { self.field_name: '' } # Returning None means no error return None
def update(self): form = LoadFromSimilarForBookForm(self.context, self.request) view = LoadFromSimilarForBookSubView(self.context, self.request) view = view.__of__(self.context) view.form_instance = form self.loadsimilarform = view form.parent_form = self hiddenFields = ['related_aleph_record', 'summary_aleph_record', 'isClosed', 'thumbnail', 'shouldBeFullyCatalogized', 'isWellFormedForLTP', 'voucher'] for field in hiddenFields: if field in self.fields: del self.fields[field] super(AddAtOnceForm,self).update() sdm = self.context.session_data_manager session = sdm.getSessionData(create=True) proper_record = session.get('proper_record',None) # proper_record = getAlephRecord() if proper_record: messages = IStatusMessage(self.request) messages.addStatusMessage(u"Formulář je předvyplněn vybraným záznamem z Alephu.", type="info") self.loadValuesFromAlephRecord(proper_record) session.set('proper_record',None)
def handleApply(self, action): data, errors = self.extractData() # get write access to the database portal_state = getMultiAdapter( (self.context, self.request), name=u'plone_portal_state' ) navigation_root = portal_state.navigation_root() writer = IEventDatabaseWriteProvider(navigation_root) if errors: self.status = self.formErrorsMessage return for key in EventOrgEditForm.context_attributes: value = data.get(key) setattr(self.context, key, value) org_data = {} for key in EventOrgEditForm.database_attributes: org_data[key] = data.get(key) if writer.db_org_id == 0: db_org_id = writer.insertOrg(**org_data) setattr(navigation_root, 'dbOrgId', db_org_id) else: writer.updateOrgData(**org_data) writer.updateOrgCats(data.get('org_categories', [])) messages = IStatusMessage(self.request) messages.add(u"Organization event settings updated", type=u"info") self.request.response.redirect("caledit")
def handleBuild(self, action): """\ Build the thing """ # validate all groups. errors = extractError(self) if errors: self.status = _(u"Unable to build exposure due to input error; " "please review the form and make the appropriate changes, " "update each subsection using the provided button, and try " "again.") return wh = zope.component.getAdapter(self.context, IExposureWizard) try: moldExposure(self.context, self.request, wh.structure) except ProcessingError, e: # XXX almost certainly a bad idea but this fixes # tracker item 3610. import transaction transaction.doom() self._doomed = True status = IStatusMessage(self.request) status.addStatusMessage(_(str(e)), 'error') # maybe the subscriber to this can do the rollback? raise ActionExecutionError(e)
def update(self): if self.portal_state is None: self.portal_state = component.getMultiAdapter( (self.context, self.request), name=u'plone_portal_state' ) if self.portal_registry is None: self.portal_registry = component.getUtility(IRegistry) if self.embed_settings is None: self.embed_settings = {} registry = self.portal_registry self.embed_settings = registry.forInterface(EtherpadEmbedSettings) if self.etherpad_settings is None: registry = self.portal_registry self.etherpad_settings = registry.forInterface(EtherpadSettings) if self.etherpad is None: self.etherpad = HTTPAPI(self.context, self.request) self.etherpad.update() try: self.etherpad.checkToken() except ValueError: status = IStatusMessage(self.request) msg = _(u"Etherpad connection error") status.add(msg) self.etherpad_iframe_url = "" return self._updateName() self._updateAuthor() self._updateId() self._updateSession() self._updateIframe()
def __call__(self): # Add a status message, showing whether the login has # succeeded or not. status = IStatusMessage(self.request) pps = getMultiAdapter((self.context, self.request), name='plone_portal_state') anonymous = pps.anonymous() if anonymous and not self.show_group_options(): success = False msg = PMF(u"Login failed") msg = translate(msg, 'plone', context=self.request) status.addStatusMessage(msg, type='error') elif anonymous: success = False else: success = True msg = PMF(u"heading_you_are_now_logged_in", default=u"You are now logged in") msg = translate(msg, 'plone', context=self.request) status.addStatusMessage(msg, type='info') if anonymous and self.request.cookies.get('aselectattributes'): success = False logger.debug("We have an A-Select cookie but are still anonymous.") if (not success) or self.request.get('noredirect'): # Show the template. Manually adding '?noredirect=1' to # the url may be handy for debugging: this way you stay on # this url, which means you can inspect the cookies. return self._template() logger.debug('Redirecting to Plone Site root.') # We are only defined on the Plone Site root, so we can just # redirect to the context. self.request.RESPONSE.redirect(self.context.absolute_url())
def add(self): request = self.request view = request.get('view', '') super(FavoriteActions, self).add() statusmsg = IStatusMessage(request) statusmsg.add(_("The item has been added to your favorites")) request.response.redirect(self.context.absolute_url() + '/' + view)
def __call__(self): self.update() message = _('Feed items updated') messages = IStatusMessage(self.request, alternate=None) if messages is not None: messages.addStatusMessage(message, 'info') self.request.response.redirect(self.context.absolute_url())
def setProperties(self): context = aq_inner(self.context) new = self.request.form.get('new_definition', None) columns = self.request.form.get('new_definition_columns', []) error = 0 if new and new['name']: try: self._definitions.addDefinition(new['name'], new['style'], self._formatColumns(columns)) except: transaction.abort() error = _(u'Unable to parse the columns field of the definition to be added') modify = self.request.form.get('definitions', [])[:] for definition in modify: if definition.has_key('delete'): self._definitions.removeDefinition(definition['id']) if definition.has_key('delete'): continue try: columns = self.request.form.get('definition_columns_%s' % definition['origname'], []) self._definitions.addDefinition(definition['name'], definition['style'], self._formatColumns(columns), definition['id']) except: transaction.abort() error = _(u'Unable to parse the columns field of one of the definitions to be modified') statusmessage = IStatusMessage(self.request) if error: statusmessage.addStatusMessage(error, 'error') else: statusmessage.addStatusMessage(_p(u'Changes saved.'), 'info')
def forceUpdateProfile(principal, event): """ Redirect to edit profile if user has not completed info about thematic and institutional domains """ site = getSite() if site is not None: membership = getToolByName(site, 'portal_membership') authenticated_user = membership.getAuthenticatedMember() t_d = authenticated_user.getProperty('thematic_domain', '') i_d = authenticated_user.getProperty('institutional_domain', '') if t_d == '' or i_d == '': request = getRequest() messages = IStatusMessage(request) messages.add( u"Please complete your profile adding your Professional " + "thematic domain and Institutional domain.", type=u"info") if request.get('came_from', None): request['came_from'] = '' request.form['came_from'] = '' request.form['next'] = '' edit_profile_url = site.portal_url() + '/@@personal-information' request.RESPONSE.redirect(edit_profile_url)
def update(self): context = aq_inner(self.context) self.helper = MultiLinguageContentListingHelper(context, self.request) if not self.helper.getSettings(): messages = IStatusMessage(self.request) messages.addStatusMessage(u"Silvuple not configured in Site Setup", type="error")
def PurgeAction(self): """ Purging actual object's cache by sending prepared request. The example request: PURGE 5bf8c564b5da741f9bc9a14ca2509b94 HTTP/1.1 Host: localhost:6081 """ mtool = getToolByName(self, 'portal_membership') host, port = self.getVarnishAddress() if not mtool.checkPermission("Modify portal content", self.context): raise Unauthorized purging_uids = [getUidOrId(self.context)] context_state = getMultiAdapter((self.context, self.request), name='plone_context_state') if context_state.is_default_page(): purging_uids.append(getUidOrId(context_state.parent())) #TODO: Handle exceptions of connection problem for uid in purging_uids: connection = httplib.HTTPConnection(host, port) connection.putrequest('PURGE', uid) connection.putheader('Host', host + ':' + port) connection.endheaders() connection.send('') response = connection.getresponse() messages = IStatusMessage(self.request) messages.addStatusMessage(_("Cache of the page has been purged"), type="info") return self.request.RESPONSE.redirect(self.context.absolute_url())
def render(self): this_attendee = self.context trainings_uid = self.request.form.get('trainings_uid', []) if isinstance(trainings_uid, str): trainings_uid = [trainings_uid, ] seat_table = SeatTable(this_attendee) # confirmed trainings are always part of the selection all_trainings = list(this_attendee.confirmed_trainings()) for uid in trainings_uid: if uid not in all_trainings: # was this training already ours # OR are there still available seats? if uid in this_attendee.trainings or seat_table.available_seats(uid): all_trainings.append(uid) else: training = uuidToObject(uid) messages = IStatusMessage(self.request) warning = _(u'This training is already fully booked') + ': ' + training.title messages.addStatusMessage(warning, type="warning") # reserve a seat for each training seat_table.refresh_attendee_trainings(this_attendee, all_trainings) # store the list of trainings of this attendee this_attendee.trainings = all_trainings this_attendee.reindexObject(idxs=['trainings', ]) # set the time of this operation for use in liberation of blocked seats this_attendee.last_time_trainings_were_set = datetime.now() if self.request.get('finish_button'): # back to the registration return self.request.response.redirect(this_attendee.getParentNode().absolute_url()) else: return self.request.response.redirect(this_attendee.absolute_url())
def send_feedback(self): IStatusMessage(self.request).add( _(u'A mail has now been sent to the site administrator ' u'regarding your questions and/or comments.') )
class BaseRegistrationForm(AutoExtensibleForm, form.Form): """Form to be used as base for Register and Add User forms.""" label = u"" description = u"" formErrorsMessage = _('There were errors.') ignoreContext = True enableCSRFProtection = True schema = ICombinedRegisterSchema # this attribute indicates if user was successfully registered _finishedRegister = False def __init__(self, *args, **kwargs): super(BaseRegistrationForm, self).__init__(*args, **kwargs) self.schema = getUtility(IRegisterSchemaProvider).getSchema() def _get_security_settings(self): """Return security settings from the registry.""" registry = getUtility(IRegistry) return registry.forInterface(ISecuritySchema, prefix="plone") def render(self): if self._finishedRegister: return self.context.unrestrictedTraverse('registered')() return super(BaseRegistrationForm, self).render() def updateFields(self): """Fields are dynamic in this form, to be able to handle different join styles. """ settings = self._get_security_settings() use_email_as_login = settings.use_email_as_login # Filter schema for registration omitted = [] default_fields = IUserDataSchema.names() + IRegisterSchema.names() for name in self.schema: # we always preserve default fields if name in default_fields: omit = False else: forms_selection = getattr(self.schema[name], 'forms_selection', []) if u'On Registration' in forms_selection: omit = False else: omit = True omitted.append((Interface, name, omit)) self.schema.setTaggedValue(OMITTED_KEY, omitted) # Finally, let autoform process the schema and any FormExtenders do # their thing super(BaseRegistrationForm, self).updateFields() if use_email_as_login: self.fields['email'].field.description = _( u'help_email_creation_for_login', default=u"Enter an email " "address. This will be your login name. We respect your " "privacy, and will not give the address away to any third " "parties or expose it anywhere.") del self.fields['username'] else: self.fields['email'].field.description = _( u'help_email_creation', default=u"Enter an email address. This is necessary in case " u"the password is lost. We respect your privacy, and " u"will not give the address away to any third parties " u"or expose it anywhere.") # Change the password description based on PAS Plugin The user needs a # list of instructions on what kind of password is required. We'll # reuse password errors as instructions e.g. "Must contain a letter and # a number". Assume PASPlugin errors are already translated if self.fields.get('password', None): registration = getToolByName(self.context, 'portal_registration') err_str = registration.testPasswordValidity('') if err_str: msg = _(u'help_password_creation_with_errors', default=u'Enter your new password. ${errors}', mapping=dict(errors=err_str)) self.fields['password'].field.description = msg def generate_user_id(self, data): """Generate a user id from data. We try a few options for coming up with a good user id: 1. We query a utility, so integrators can register a hook to generate a user id using their own logic. 2. If use_uuid_as_userid is set in the registry, we generate a uuid. 3. If a username is given and we do not use email as login, then we simply return that username as the user id. 4. We create a user id based on the full name, if that is passed. This may result in an id like bob-jones-2. When the email address is used as login name, we originally used the email address as user id as well. This has a few possible downsides, which are the main reasons for the new, pluggable approach: - It does not work for some valid email addresses. - Exposing the email address in this way may not be wanted. - When the user later changes his email address, the user id will still be his old address. It works, but may be confusing. Another possibility would be to simply generate a uuid, but that is ugly. We could certainly try that though: the big plus here would be that you then cannot create a new user with the same user id as a previously existing user if this ever gets removed. If you would get the same id, this new user would get the same global and local roles, if those have not been cleaned up. When a user id is chosen, the 'user_id' key of the data gets set and the user id is returned. """ generator = queryUtility(IUserIdGenerator) if generator: userid = generator(data) if userid: data['user_id'] = userid return userid settings = self._get_security_settings() if settings.use_uuid_as_userid: userid = uuid_userid_generator() data['user_id'] = userid return userid # We may have a username already. userid = data.get('username') if userid: # If we are not using email as login, then this user name is fine. if not settings.use_email_as_login: data['user_id'] = userid return userid # First get a default value that we can return if we cannot # find anything better. pas = getToolByName(self.context, 'acl_users') email = pas.applyTransform(data.get('email')) default = data.get('username') or email or '' data['user_id'] = default fullname = data.get('fullname') if not fullname: return default userid = normalizeString(fullname) # First check that this is a valid member id, regardless of # whether a member with this id already exists or not. We # access an underscore attribute of the registration tool, so # we take a precaution in case this is ever removed as an # implementation detail. registration = getToolByName(self.context, 'portal_registration') if hasattr(registration, '_ALLOWED_MEMBER_ID_PATTERN'): if not registration._ALLOWED_MEMBER_ID_PATTERN.match(userid): # If 'bob-jones' is not good then 'bob-jones-1' will not # be good either. return default if registration.isMemberIdAllowed(userid): data['user_id'] = userid return userid # Try bob-jones-1, bob-jones-2, etc. idx = 1 while idx <= RENAME_AFTER_CREATION_ATTEMPTS: new_id = "%s-%d" % (userid, idx) if registration.isMemberIdAllowed(new_id): data['user_id'] = new_id return new_id idx += 1 # We cannot come up with a nice id, so we simply return the default. return default def generate_login_name(self, data): """Generate a login name from data. Usually the login name and user id are the same, but this is not necessarily true. When using the email address as login name, we may have a different user id, generated by calling the generate_user_id method. We try a few options for coming up with a good login name: 1. We query a utility, so integrators can register a hook to generate a login name using their own logic. 2. If a username is given and we do not use email as login, then we simply return that username as the login name. 3. When using email as login, we use the email address. In all cases, we call PAS.applyTransform on the login name, if that is defined. This is a recent addition to PAS, currently under development. When a login name is chosen, the 'login_name' key of the data gets set and the login name is returned. """ pas = getToolByName(self.context, 'acl_users') generator = queryUtility(ILoginNameGenerator) if generator: login_name = generator(data) if login_name: login_name = pas.applyTransform(login_name) data['login_name'] = login_name return login_name # We may have a username already. login_name = data.get('username') login_name = pas.applyTransform(login_name) data['login_name'] = login_name settings = self._get_security_settings() # If we are not using email as login, then this user name is fine. if not settings.use_email_as_login: return login_name # We use email as login. login_name = data.get('email') login_name = pas.applyTransform(login_name) data['login_name'] = login_name return login_name # Actions validators def validate_registration(self, action, data): """Specific business logic for this join form. Note: all this logic was taken directly from the old validate_registration.py script in Products/CMFPlone/skins/plone_login/join_form_validate.vpy """ # CSRF protection CheckAuthenticator(self.request) registration = getToolByName(self.context, 'portal_registration') error_keys = [ error.field.getName() for error in action.form.widgets.errors ] form_field_names = [f for f in self.fields] portal = getUtility(ISiteRoot) # passwords should match if 'password' in form_field_names: assert ('password_ctl' in form_field_names) # Skip this check if password fields already have an error if not ('password' in error_keys or 'password_ctl' in error_keys): password = data.get('password') password_ctl = data.get('password_ctl') if password != password_ctl: err_str = _(u'Passwords do not match.') notifyWidgetActionExecutionError(action, 'password', err_str) notifyWidgetActionExecutionError(action, 'password_ctl', err_str) # Password field checked against RegistrationTool if 'password' in form_field_names: # Skip this check if password fields already have an error if 'password' not in error_keys: password = data.get('password') if password: # Use PAS to test validity err_str = registration.testPasswordValidity(password) if err_str: notifyWidgetActionExecutionError( action, 'password', err_str) settings = self._get_security_settings() if settings.use_email_as_login: username_field = 'email' else: username_field = 'username' # The term 'username' is not clear. It may be the user id or # the login name. So here we try to be explicit. # Generate a nice user id and store that in the data. user_id = self.generate_user_id(data) # Generate a nice login name and store that in the data. login_name = self.generate_login_name(data) # Do several checks to see if the user id and the login name # are valid. # # Skip these checks if username was already in error list. # # Note that if we cannot generate a unique user id, it is not # necessarily the fault of the username field, but it # certainly is the most likely cause in a standard Plone # setup. # check if username is valid # Skip this check if username was already in error list if username_field not in error_keys: # user id may not be the same as the portal id. if user_id == portal.getId(): err_str = _(u"This username is reserved. Please choose a " "different name.") notifyWidgetActionExecutionError(action, username_field, err_str) # Check if user id is allowed by the member id pattern. if username_field not in error_keys: if not registration.isMemberIdAllowed(user_id): err_str = _(u"The login name you selected is already in use " "or is not valid. Please choose another.") notifyWidgetActionExecutionError(action, username_field, err_str) if username_field not in error_keys: # Check the uniqueness of the login name, not only when # use_email_as_login is true, but always. pas = getToolByName(self, 'acl_users') results = pas.searchUsers(name=login_name, exact_match=True) if results: err_str = _(u"The login name you selected is already in use " "or is not valid. Please choose another.") notifyWidgetActionExecutionError(action, username_field, err_str) if 'password' in form_field_names and 'password' not in error_keys: # Admin can either set a password or mail the user (or both). if not (data['password'] or data['mail_me']): err_str = _('msg_no_password_no_mail_me', default=u"You must set a password or choose to " "send an email.") # set error on password field notifyWidgetActionExecutionError(action, 'password', err_str) notifyWidgetActionExecutionError(action, 'mail_me', err_str) @button.buttonAndHandler(_(u'label_register', default=u'Register'), name='register') def action_join(self, action): data, errors = self.extractData() # extra password validation self.validate_registration(action, data) if action.form.widgets.errors: self.status = self.formErrorsMessage return self.handle_join_success(data) self._finishedRegister = True # XXX Return somewhere else, depending on what # handle_join_success returns? came_from = self.request.form.get('came_from') if came_from: utool = getToolByName(self.context, 'portal_url') if utool.isURLInPortal(came_from): self.request.response.redirect(came_from) return '' def handle_join_success(self, data): # portal should be acquisition wrapped, this is needed for the schema # adapter below portal = getToolByName(self.context, 'portal_url').getPortalObject() registration = getToolByName(self.context, 'portal_registration') # user_id and login_name should be in the data, but let's be safe. user_id = data.get('user_id', data.get('username')) login_name = data.get('login_name', data.get('username')) # I have seen a unicode user id. I cannot reproduce it, but # let's make them strings, otherwise you run into trouble with # plone.session when trying to login. if isinstance(user_id, unicode): user_id = user_id.encode('utf8') if isinstance(login_name, unicode): login_name = login_name.encode('utf8') # Set the username for good measure, as some code may expect # it to exist and contain the user id. data['username'] = user_id # The login name may already be in the form, but not # necessarily, for example when using email as login. This is # at least needed for logging in immediately when password # reset is bypassed. We need the login name here, not the # user id. self.request.form['form.username'] = login_name password = data.get('password') or registration.generatePassword() if isinstance(password, unicode): password = password.encode('utf8') try: registration.addMember(user_id, password, REQUEST=self.request) except (AttributeError, ValueError), err: logging.exception(err) IStatusMessage(self.request).addStatusMessage(err, type="error") return if user_id != login_name: # The user id differs from the login name. Set the login # name correctly. pas = getToolByName(self.context, 'acl_users') pas.updateLoginName(user_id, login_name) # set member properties self.applyProperties(user_id, data) settings = self._get_security_settings() if data.get('mail_me') or (not settings.enable_user_pwd_choice and not data.get('password')): # We want to validate the email address (users cannot # select their own passwords on the register form) or the # admin has explicitly requested to send an email on the # 'add new user' form. try: # When all goes well, this call actually returns the # rendered mail_password_response template. As a side # effect, this removes any status messages added to # the request so far, as they are already shown in # this template. response = registration.registeredNotify(user_id) return response except ConflictError: # Let Zope handle this exception. raise except Exception: ctrlOverview = getMultiAdapter((portal, self.request), name='overview-controlpanel') mail_settings_correct = not ctrlOverview.mailhost_warning() if mail_settings_correct: # The email settings are correct, so the most # likely cause of an error is a wrong email # address. We remove the account: # Remove the account: self.context.acl_users.userFolderDelUsers( [user_id], REQUEST=self.request) IStatusMessage(self.request).addStatusMessage(_( u'status_fatal_password_mail', default=u"Failed to create your account: we were " "unable to send instructions for setting a password " "to your email address: ${address}", mapping={u'address': data.get('email', '')}), type='error') return else: # This should only happen when an admin registers # a user. The admin should have seen a warning # already, but we warn again for clarity. IStatusMessage(self.request).addStatusMessage( _(u'status_nonfatal_password_mail', default=u"This account has been created, but we " "were unable to send instructions for setting a " "password to this email address: ${address}", mapping={u'address': data.get('email', '')}), type='warning') return return
def __call__(self): # if self.descriptor.startswith('D1.'): # map to old descriptor # # self._descriptor = 'D1' # this hardcodes D1.x # # descriptors to D1 # assert self.descriptor == 'D1' if 'translate' in self.request.form: report_view = self.get_report_view() report_view.auto_translate() messages = IStatusMessage(self.request) messages.add( u"Auto-translation initiated, please refresh " u"in a couple of minutes", type=u"info") print("Will render report for: %s" % self.article) self.filename = filename = self.get_report_filename() factsheet = None source_file = ('File not found', None) multiple_source_files = False if filename: if isinstance(filename, tuple): multiple_source_files = True try: source_file = [ (f, get_report_file_url(f) + '/manage_document') for f in filename ] except: logger.exception("Error in getting HTML Factsheet URL)") else: url = get_report_file_url(filename) if url: try: factsheet = get_factsheet_url(url) except Exception: logger.exception( "Error in getting HTML Factsheet URL %s", url) else: logger.warning("No factsheet url, filename is: %r", filename) source_file = (filename, url + '/manage_document') rep_info = self.get_reporting_information() report_header_data = self.get_report_header_data( rep_info.reporters, source_file, factsheet, rep_info.report_date, multiple_source_files) report_header = self.report_header_template(**report_header_data) try: report_data, report_data_rows = self.get_report_data() except: report_data, report_data_rows = 'Error in rendering report', [] trans_edit_html = self.translate_view()() self.report_html = report_header + report_data + trans_edit_html if 'download' in self.request.form: return self.download(report_data_rows, report_header_data) return self.index()
def handleCancel(self, action): IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled'), 'info') self.request.response.redirect( '%s/%s' % (self.context.absolute_url(), self.control_panel_view))
def handleCancel(self, action): IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"), "info") self.request.response.redirect(self.nextURL()) notify(EditCancelledEvent(self.context))
class ReportStylesView(object): """View for report style control""" def __init__(self, context, request): self.context = context # IDataReport self.request = request self.status = IStatusMessage(self.request) # uncached initial values: self.show_paste = False self._contained = None self._charts = None self._stylebooks = None def can_paste_stylebooks(self): if self.request.get('__cp', None) is not None: if self.context.cb_dataValid(): oblist = self.context.cb_dataItems() if oblist: return all( map( lambda o: IChartStyleBook.providedBy(o), oblist, )) return False @property def contained(self): if self._contained is None: self._contained = self.context.objectValues() return self._contained def charts(self): if self._charts is None: self._charts = filter( lambda o: o.portal_type in CHART_TYPES, self.contained, ) return self._charts def stylebooks(self): if self._stylebooks is None: self._stylebooks = filter( lambda o: o.portal_type == STYLEBOOK_TYPE, self.contained, ) return self._stylebooks def update_apply(self, *args, **kwargs): req = self.request do_bind = bool(req.get('bind-stylebook', False)) _get = lambda name: self.context.get(name, None) stylebook = _get(req.get('selected-stylebook')) if stylebook is None: self.status.addStatusMessage('Unknonwn style book', type='info') return targets = [_get(name) for name in req.get('selected-charts', [])] if not targets: self.status.addStatusMessage( 'No target charts selected to apply stylebook to.', type='info') return for target in targets: clone_chart_styles(stylebook, target) # copy styles now if do_bind: bookuid = IUUID(stylebook, None) target.stylebook = bookuid _listcharts = lambda s: ', '.join(['"%s"' % o.Title() for o in s]) msg = 'Copied styles from' if not do_bind else 'Bound' self.status.addStatusMessage( '%s stylebook "%s" to %s charts: %s' % ( msg, stylebook.Title(), len(targets), _listcharts(targets), ), type='info', ) if do_bind: self.status.addStatusMessage( 'IMPORTANT: subsequent changes to style book and line ' 'styles will propogate to the charts listed as bound ' 'to a style book. To remove a binding, visit the Edit ' 'tab of a chart that has been bound to a style book this ' 'way.', type='info', ) def _stylebook_title(self): """get next stylebook title""" base = u'Style book %s' books = self.stylebooks() if not books: return base % 1 titles = [o.Title() for o in books] isdigit = lambda s: s in [str(i) for i in range(0, 10)] autonames = sorted([ title for title in titles if title.startswith(base[:-2]) and isdigit(title[-1]) ]) if not autonames: return base % 1 lastnum = int(autonames[-1].split(' ')[-1]) return base % (lastnum + 1) def update_mimic(self, *args, **kwargs): req = self.request mimic = req.get('existing-charts', None) mimic = self.context.get(mimic, None) if not mimic: self.status.addStatusMessage('Unable to locate existing chart', type='warning') return linecount = len(mimic.objectIds()) title = self._stylebook_title() stylebook = createContentInContainer( self.context, STYLEBOOK_TYPE, title=title, ) for i in range(linecount): createContentInContainer( stylebook, LINESTYLE_TYPE, title=u'Line style %s' % i, ) clone_chart_styles(source=mimic, target=stylebook) self._contained = self._stylebooks = None # un-cache now outdated self.status.addStatusMessage( 'Created a new stylebook "%s" based on styles of existing chart ' '"%s".' % (title, mimic.Title()), type='info', ) def update(self, *args, **kwargs): self.show_paste = self.can_paste_stylebooks() req = self.request if req.get('REQUEST_METHOD', 'GET') == 'POST': if 'apply-stylebook' in req.form or 'bind-stylebook' in req.form: self.update_apply(*args, **kwargs) if 'existing-mimic' in req.form: self.update_mimic(*args, **kwargs) def __call__(self, *args, **kwargs): self.update(*args, **kwargs) return self.index(*args, **kwargs) # provided by template via Five
class ComposedForm(AutoExtensibleForm, form.Form): """ A form composed from multiple schema adapting a form definition. This composition uses (base class from) plone.autoform to compose a merged form. """ ignoreContext = True # form operates without edit context. # autoGroups here requires plone.autoform >= 1.6 for anonymous schema: autoGroups = True enable_form_tabbing = False # do not display fieldsets in tabs. # schema must be property, not attribute for AutoExtensibleForm sublcass @property def schema(self): return self._schema @property def additionalSchemata(self): return self._additionalSchemata def __init__(self, context, request, name=None): """ Construct composed form given (default) schema an a tuple of ordered additional schema key/value pairs of (string) component name keys to schema values. """ self.context = context self.request = request # form definition will either be context, or adaptation of context. # see uu.formlibrary.definition.form_definition for adapter example. if name is None: self.definition = IFormDefinition(self.context) else: self.definition = queryAdapter( self.context, IFormDefinition, name=name, ) self._schema = self.definition.schema self.groups = [] # modified by updateFieldsFromSchemata() self.components = IFormComponents(self.definition) self.group_schemas = self._group_schemas() self.group_titles = self._group_titles() # mapping: schema to names: self.schema_names = dict(invert(self.group_schemas)) # ordered list of additional schema for AutoExtensibleForm: self._additionalSchemata = tuple( [t[1] for t in self.group_schemas if t[0]]) # super(ComposedForm, self).__init__(self, context, request) form.Form.__init__(self, context, request) self.saved = False # initial value: no duplication of save... self.save_attempt = False # flag for save attempt, success or not self._status = IStatusMessage(self.request) def _group_schemas(self): result = [] for name in self.components.names: group = self.components.groups[name] schema = group.schema if group.group_usage == u'grid': schema = grid_wrapper_schema(schema) result.append((name, schema)) return result def _group_titles(self): result = {} for name, group in self.components.groups.items(): result[name] = group.Title() return result def updateFieldsFromSchemata(self): self.groups = [] for name, schema in self.group_schemas: if name == '': continue # default, we don't need another group title = self.group_titles.get(name, name) fieldset_group = GroupFactory(name, field.Fields(), title) self.groups.append(fieldset_group) super(ComposedForm, self).updateFieldsFromSchemata() def _load_widget_data(self): _marker = object() data = aq_base(self.context).data groups = dict((g.__name__, g) for g in self.groups) groupnames = [''] + groups.keys() for groupname in groupnames: group_data = data.get(groupname, None) if groupname is '': group = self else: group = groups.get(groupname) fieldgroup = self.definition[groupname] # # plone.autoform binds groups really, really late, so # # we are stuck with a GroupFactory object, we need to # # call it to get the actual group, then replace the # # group factory with it once we have manipulated # # any widget values: if IGroupFactory.providedBy(group): idx = self.groups.index(group) actual_group = group(self.context, self.request, self) self.groups[idx] = group = actual_group group.update() # will populate group.widgets if group_data and fieldgroup.group_usage == 'grid': data_widget = group.widgets.values()[0] data_widget.value = getattr(group_data, 'data', []) continue if group_data is not None: for formfield in group.fields.values(): schema_field = formfield.field widgetname = formfield.__name__ fieldname = schema_field.__name__ v = getattr(group_data, fieldname, _marker) if v is not _marker: widget = group.widgets.get(widgetname) conv = getMultiAdapter( (schema_field, widget), IDataConverter, ) if not IDataGridField.providedBy(widget): v = conv.toWidgetValue(v) widget.value = v if hasattr(widget, 'update'): # may be necessary to copy value to other state, # as is the case with radio button widgets widget.update() # multiple collection like set/list (multi-choice) # has issues where SequenceWidget wants to reset # widget.value during update... so we have to # check the value (ugly hack) and also re-set the # value for the display widget: if ICollection.providedBy(schema_field): widget.value = v if v: term_item = [ t for t in widget.items if t.get('value') == v[0] ][0] term_item['checked'] = True def updateWidgets(self): common_widget_updates(self) for group in self.groups: common_widget_updates(group) super(ComposedForm, self).updateWidgets() # # finally, if non-defintion context, set widget values via group data if not IFormDefinition.providedBy(self.context): self._load_widget_data() def datagridInitialise(self, subform, widget): if not hasattr(self, '_widgets_initialized'): self._widgets_initialized = [] # don't duplicate effort! if subform not in self._widgets_initialized: date_fields = [ f for f in subform.fields.values() if IDate.providedBy(f.field) ] for formfield in date_fields: formfield.widgetFactory = TypeADateFieldWidget self._widgets_initialized.append(subform) def getPrefix(self, schema): if schema in self.schema_names: return self.schema_names[schema] # fall-back will not work for anoymous schema without names, but # it is the best we can assume to do here: return super(ComposedForm, self).getPrefix(schema) def _saveResult(self, result): schemas = dict(self.group_schemas) for name, values in result.items(): name = str(name) schema = schemas.get(name, self._schema) # schema or default group if schema: group_record = self.context.data.get(name, None) if group_record is None: group_record = self.context.data[name] = FormEntry() group_record.sign(schema) for fieldname, value in values.items(): setattr(group_record, fieldname, value) def _handleSave(self, action, msg='Saved form data'): self.save_attempt = True data, errors = self.extractData() if errors or IFormDefinition.providedBy(self.context) or self.saved: return False # just validate if errors, or if context if defn if not self.saved: result = {} # submitted data. k: group name; v: dict of name/value group_keys = [] for group in self.groups: groupdata = {} form_group_data = group.extractData()[0] for name, _field in group.fields.items(): group_keys.append(name) fieldname = _field.field.__name__ default = getattr(_field.field, 'default', None) field_data = form_group_data.get(name, default) if ICollection.providedBy(_field.field): if isinstance(_field.field.value_type, DictRow): is_nonempty_row = lambda v: any(v.values()) field_data = filter(is_nonempty_row, field_data) groupdata[fieldname] = field_data result[group.__name__] = groupdata # filter default fieldset values, ignore group values in data dict: result[''] = dict([(k, v) for k, v in data.items() if k not in group_keys]) self._saveResult(result) self.saved = True history_log(self.context, message=msg, set_modified=True) notify(ObjectModifiedEvent(self.context)) transaction.get().note(msg) self._status.addStatusMessage('Saved form data', type='info') return True @button.buttonAndHandler(u'\u2714 Save draft', condition=lambda form: form.mode == 'input') def handleSave(self, action): self._handleSave(action) @button.buttonAndHandler(u'\u21E5 Save and submit', condition=lambda form: form.mode == 'input') def handleSaveSubmit(self, action): saved = self._handleSave(action) if not saved: return # no status change, no 302 -- validation errors to user wftool = getToolByName(self.context, 'portal_workflow') chain = wftool.getChainFor(self.context)[0] state = wftool.getStatusOf(chain, self.context)['review_state'] if state == 'visible': wftool.doActionFor(self.context, 'submit') self.context.reindexObject() self._status.addStatusMessage('Form submitted for review', type='info') url = self.context.absolute_url() self.request.RESPONSE.redirect(url)
def enumerateGroups(self, id=None, exact_match=False, sort_by=None, max_results=None, **kw): """ -> ( group_info_1, ... group_info_N ) o Return mappings for groups matching the given criteria. o 'id' in combination with 'exact_match' true, will return at most one mapping per supplied ID ('id' and 'login' may be sequences). o If 'exact_match' is False, then 'id' may be treated by the plugin as "contains" searches (more complicated searches may be supported by some plugins using other keyword arguments). o If 'sort_by' is passed, the results will be sorted accordingly. known valid values are 'id' (some plugins may support others). o If 'max_results' is specified, it must be a positive integer, limiting the number of returned mappings. If unspecified, the plugin should return mappings for all groups satisfying the criteria. o Minimal keys in the returned mappings: 'id' -- (required) the group ID 'pluginid' -- (required) the plugin ID (as returned by getId()) 'properties_url' -- (optional) the URL to a page for updating the group's properties. 'members_url' -- (optional) the URL to a page for updating the principals who belong to the group. o Plugin *must* ignore unknown criteria. o Plugin may raise ValueError for invalid critera. o Insufficiently-specified criteria may have catastrophic scaling issues for some implementations. """ default = () if not self.is_plugin_active(pas_interfaces.IGroupEnumerationPlugin): return default groups = self.groups if not groups: return default if id and exact_match: if id in self.getGroupIds(): return ({'id': id, 'pluginid': self.getId()}) else: return default elif id: kw['id'] = id if not kw: # show all matches = groups.ids else: matches = [] cookie = None while True: try: res = groups.search( criteria=encode(kw), attrlist=None, exact_match=exact_match, page_size=self._ldap_props.page_size, cookie=cookie, ) if isinstance(res, tuple): batch_matches, cookie = res else: batch_matches, cookie = res, '' except ValueError: return default matches += batch_matches if not cookie: break if len(matches) >= 100: msg = 'Too many search results. Please, narrow your search.' IStatusMessage(self.REQUEST).add(msg, type='warning') return default if sort_by == 'id': matches = sorted(matches) pluginid = self.getId() ret = list() if exact_match: for id, attrs in matches: ret.append({'id': id, 'pluginid': pluginid}) else: for id in matches: ret.append({'id': id, 'pluginid': pluginid}) if max_results and len(ret) > max_results: ret = ret[:max_results] return ret
def update(self): # XXX: complexity too high: refactoring needed processInputs(self.request) self._setup() self.errors = {} form = self.request.form if 'form.button.Cancel' in form: IStatusMessage(self.request).add(_(u"Changes cancelled")) self.redirect("{0}/@@overview-controlpanel".format(self.site_url)) return False if 'form.button.Enable' in form: self.authorize() themeSelection = form.get('themeName', None) if themeSelection: themeData = self.getThemeData(self.availableThemes, themeSelection) applyTheme(themeData) self.theme_settings.enabled = True IStatusMessage(self.request).add( _(u"Theme enabled. Note that this control panel page is " u"never themed.")) self._setup() return True if 'form.button.InvalidateCache' in form: self.authorize() policy = theming_policy() policy.invalidateCache() return True if 'form.button.Disable' in form: self.authorize() applyTheme(None) self.theme_settings.enabled = False IStatusMessage(self.request).add(_(u"Theme disabled.")) self._setup() return True if 'form.button.AdvancedSave' in form: self.authorize() self.theme_settings.readNetwork = form.get('readNetwork', False) themeEnabled = form.get('themeEnabled', False) rules = form.get('rules', None) prefix = form.get('absolutePrefix', None) doctype = str(form.get('doctype', "")) hostnameBlacklist = form.get('hostnameBlacklist', []) parameterExpressions = {} parameterExpressionsList = form.get('parameterExpressions', []) for line in parameterExpressionsList: try: name, expression = line.split('=', 1) name = str(name.strip()) expression = str(expression.strip()) parameterExpressions[name] = expression except ValueError: message = _( 'error_invalid_parameter_expressions', default=u"Please ensure you enter one expression per " u"line, in the format <name> = <expression>.") self.errors['parameterExpressions'] = message themeBase = form.get('themeBase', None) markSpecialLinks = form.get('markSpecialLinks', None) extLinksOpenInNewWindow = form.get('extLinksOpenInNewWindow', None) if not self.errors: # Trigger onDisabled() on plugins if theme was active # previously and rules were changed if self.theme_settings.rules != rules: applyTheme(None) self.theme_settings.enabled = themeEnabled self.theme_settings.rules = rules self.theme_settings.absolutePrefix = prefix self.theme_settings.parameterExpressions = parameterExpressions self.theme_settings.hostnameBlacklist = hostnameBlacklist self.theme_settings.doctype = doctype # Theme base settings if themeBase is not None: self.pskin.default_skin = themeBase.encode('utf-8') if markSpecialLinks is not None: self.mark_special_links = markSpecialLinks if extLinksOpenInNewWindow is not None: self.ext_links_open_new_window = extLinksOpenInNewWindow IStatusMessage(self.request).add(_(u"Changes saved")) self._setup() return True else: IStatusMessage(self.request).add(_(u"There were errors"), 'error') self.redirectToFieldset('advanced') return False if 'form.button.Import' in form: self.authorize() enableNewTheme = form.get('enableNewTheme', False) replaceExisting = form.get('replaceExisting', False) themeArchive = form.get('themeArchive', None) themeZip = None performImport = False try: themeZip = zipfile.ZipFile(themeArchive) except ( zipfile.BadZipfile, zipfile.LargeZipFile, ): logger.exception("Could not read zip file") self.errors['themeArchive'] = _( 'error_invalid_zip', default=u"The uploaded file is not a valid Zip archive") if themeZip: try: themeData = extractThemeInfo(themeZip, checkRules=False) except ( ValueError, KeyError, ), e: logger.warn(str(e)) self.errors['themeArchive'] = _( 'error_no_rules_file', u"The uploaded file does not contain a valid theme " u"archive.") else: themeContainer = getOrCreatePersistentResourceDirectory() themeExists = themeData.__name__ in themeContainer if themeExists: if not replaceExisting: self.errors['themeArchive'] = _( 'error_already_installed', u"This theme is already installed. Select " u"'Replace existing theme' and re-upload to " u"replace it.") else: del themeContainer[themeData.__name__] performImport = True else: performImport = True if performImport: themeContainer.importZip(themeZip) themeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, themeData.__name__) if themeDirectory is not None: # If we don't have a rules file, use the template if themeData.rules == u"/++{0:s}++{1:s}/{2:s}".format( THEME_RESOURCE_NAME, themeData.__name__, RULE_FILENAME, ) and not themeDirectory.isFile(RULE_FILENAME): templateThemeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, TEMPLATE_THEME) themeDirectory.writeFile( RULE_FILENAME, templateThemeDirectory.readFile(RULE_FILENAME)) if not themeDirectory.isFile(DEFAULT_THEME_FILENAME): IStatusMessage(self.request).add( _(u"A boilerplate rules.xml was added to " u"your theme, but no index.html file " u"found. Update rules.xml to reference " u"the current theme file."), 'warning', ) plugins = getPlugins() pluginSettings = getPluginSettings(themeDirectory, plugins) if pluginSettings is not None: for name, plugin in plugins: plugin.onCreated(themeData.__name__, pluginSettings[name], pluginSettings) if enableNewTheme: applyTheme(themeData) self.theme_settings.enabled = True if not self.errors: self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( self.site_url, themeData.__name__)) return False else: IStatusMessage(self.request).add(_(u"There were errors"), "error") self.renderOverlay('upload') return True
def enumerateUsers(self, id=None, login=None, exact_match=False, sort_by=None, max_results=None, **kw): """-> ( user_info_1, ... user_info_N ) o Return mappings for users matching the given criteria. o 'id' or 'login', in combination with 'exact_match' true, will return at most one mapping per supplied ID ('id' and 'login' may be sequences). o If 'exact_match' is False, then 'id' and / or login may be treated by the plugin as "contains" searches (more complicated searches may be supported by some plugins using other keyword arguments). o If 'sort_by' is passed, the results will be sorted accordingly. known valid values are 'id' and 'login' (some plugins may support others). o If 'max_results' is specified, it must be a positive integer, limiting the number of returned mappings. If unspecified, the plugin should return mappings for all users satisfying the criteria. o Minimal keys in the returned mappings: 'id' -- (required) the user ID, which may be different than the login name 'login' -- (required) the login name 'pluginid' -- (required) the plugin ID (as returned by getId()) 'editurl' -- (optional) the URL to a page for updating the mapping's user o Plugin *must* ignore unknown criteria. o Plugin may raise ValueError for invalid criteria. o Insufficiently-specified criteria may have catastrophic scaling issues for some implementations. """ default = tuple() if not self.is_plugin_active(pas_interfaces.IUserEnumerationPlugin): return default # TODO: sort_by in node.ext.ldap if login: if not isinstance(login, basestring): # XXX TODO raise NotImplementedError('sequence is not supported yet.') kw['login'] = login # pas search users gives both login and name if login is meant if "login" in kw and "name" in kw: del kw["name"] if id: if not isinstance(id, basestring): # XXX TODO raise NotImplementedError('sequence is not supported yet.') kw['id'] = id users = self.users if not users: return default if not exact_match: for value in users.principal_attrmap.values(): kw[value] = kw.values()[0] matches = [] cookie = None while True: try: res = users.search( criteria=encode(kw), attrlist=exact_match and users.context.search_attrlist or ['login'], exact_match=exact_match, or_search=not exact_match, page_size=not exact_match and self._ldap_props.page_size or None, cookie=cookie, ) if isinstance(res, tuple): batch_matches, cookie = res else: batch_matches, cookie = res, '' except ValueError: return default matches += batch_matches if not cookie: break if len(matches) >= 100: msg = 'Too many search results. Please, narrow your search.' IStatusMessage(self.REQUEST).add(msg, type='warning') return default pluginid = self.getId() ret = list() for id, attrs in matches: if 'login' not in attrs: continue ret.append({ 'id': id, 'login': attrs['login'][0], 'pluginid': pluginid }) if max_results and len(ret) > max_results: ret = ret[:max_results] return ret
def __call__(self): self.request.set('disable_border', True) self.errors = {} props = getToolByName(self.context, 'portal_properties').pcommerce_properties if self.request.form.has_key('pcommerce_save'): adapter = ITaxes(self.context) taxes = [] raw = self.request.form.get('taxes', []) for tax in raw: if not tax.has_key('remove') or not tax['remove']: try: tax = { 'id': tax['id'], 'tax': float(tax['tax']), 'zone': tax['zone'], 'taxname': tax['taxname'] } if tax['zone'] == '': self.errors[tax['id']] = _( u'Please provide a zone name') elif tax['taxname'] == '': self.errors[tax['id']] = _( u'Please provide a tax name') if not self.errors.has_key(tax['id']): taxes.append(tax) except: self.errors[tax['id']] = _( u'Please enter a floating point number (e.g. 7.6)') for prop in self.properties: self.values[prop] = self.request.form.get(prop, '') taxincl = None tax = self.request.form.get('taxincl.tax', '') taxname = self.request.form.get('taxincl.taxname', '') try: if taxname == '' and tax != '': self.errors['taxincl'] = _(u'Please provide a tax name') else: if tax == '': tax = 0.0 taxincl = (float(tax), taxname) except: self.errors['taxincl'] = _( u'Please enter a floating point number (e.g. 7.6)') if not self.errors: adapter.edit(taxes) adapter.taxincl = taxincl IStatusMessage(self.request).addStatusMessage( _p('Properties saved'), 'info') for prop in self.properties: if prop == 'columns': self.values[prop] = int(self.values[prop]) props._setPropValue(prop, self.values[prop]) else: IStatusMessage(self.request).addStatusMessage( _p(u'Please correct the indicated errors'), 'error') for prop in self.properties: self.values[prop] = props.getProperty(prop, '') return self.template()
def handle_continue(self, action): data, errors = self.extractData() if not errors: oguid = ISuccessorTaskController(self.context).get_oguid() # set forwarding flag if self.context.task_type == 'forwarding_task_type': is_forwarding = True else: is_forwarding = False dm = getUtility(IWizardDataStorage) dmkey = 'accept:%s' % oguid dm.set(dmkey, u'is_forwarding', is_forwarding) dm.set(dmkey, u'is_only_assign', False) dm.update(dmkey, data) method = data.get('method') if method == 'participate': accept_task_with_response(self.context, data['text']) IStatusMessage(self.request).addStatusMessage( _(u'The task has been accepted.'), 'info') return self.request.response.redirect( self.context.absolute_url()) elif method == 'forwarding_participate': """only store the forwarding in the inbox and create a successor forwrding""" admin_unit = self.context.get_responsible_admin_unit() # push session data to target unit dm.push_to_remote_client(dmkey, admin_unit.id()) url = '%s/@@accept_store_in_inbox?oguid=%s' % ( admin_unit.public_url, oguid) return self.request.RESPONSE.redirect(url) elif method == 'existing_dossier': admin_unit = self.context.get_responsible_admin_unit() # push session data to target unit dm.push_to_remote_client(dmkey, admin_unit.id()) url = '%s/@@accept_choose_dossier?oguid=%s' % ( admin_unit.public_url, oguid) return self.request.RESPONSE.redirect(url) elif method == 'new_dossier': admin_unit = self.context.get_responsible_admin_unit() oguid = ISuccessorTaskController(self.context).get_oguid() # push session data to target client dm.push_to_remote_client(dmkey, admin_unit.id()) url = '/'.join( (admin_unit.public_url, '@@accept_select_repositoryfolder?oguid=%s' % oguid)) return self.request.RESPONSE.redirect(url)
def message(self, mensagem): messages = IStatusMessage(self.request) messages.add(mensagem, type='info') return
class ThemingControlpanel(BrowserView): @property def site_url(self): """Return the absolute URL to the current site, which is likely not necessarily the portal root. """ return getSite().absolute_url() def __call__(self): self.pskin = getToolByName(self.context, 'portal_skins') if self.update(): return self.index() return '' def _setup(self): registry = getUtility(IRegistry) self.theme_settings = registry.forInterface(IThemeSettings, False) self.link_settings = registry.forInterface(ILinkSchema, prefix="plone", check=False) self.zodbThemes = getZODBThemes() self.availableThemes = getAvailableThemes() self.selectedTheme = self.getSelectedTheme( self.availableThemes, self.theme_settings.currentTheme, self.theme_settings.rules) self.overlay = '' self.skinsVocabulary = getUtility(IVocabularyFactory, name='plone.app.vocabularies.Skins')( self.context) # Set response header to make sure control panel is never themed self.request.response.setHeader('X-Theme-Disabled', '1') def redirect(self, url): self.request.response.redirect(url) def get_mark_special_links(self): return self.link_settings.mark_special_links def set_mark_special_links(self, value): self.link_settings.mark_special_links = value mark_special_links = property(get_mark_special_links, set_mark_special_links) def get_ext_links_open_new_window(self): return self.link_settings.external_links_open_new_window def set_ext_links_open_new_window(self, value): self.link_settings.external_links_open_new_window = value ext_links_open_new_window = property(get_ext_links_open_new_window, set_ext_links_open_new_window) def update(self): # XXX: complexity too high: refactoring needed processInputs(self.request) self._setup() self.errors = {} form = self.request.form if 'form.button.Cancel' in form: IStatusMessage(self.request).add(_(u"Changes cancelled")) self.redirect("{0}/@@overview-controlpanel".format(self.site_url)) return False if 'form.button.Enable' in form: self.authorize() themeSelection = form.get('themeName', None) if themeSelection: themeData = self.getThemeData(self.availableThemes, themeSelection) applyTheme(themeData) self.theme_settings.enabled = True IStatusMessage(self.request).add( _(u"Theme enabled. Note that this control panel page is " u"never themed.")) self._setup() return True if 'form.button.InvalidateCache' in form: self.authorize() policy = theming_policy() policy.invalidateCache() return True if 'form.button.Disable' in form: self.authorize() applyTheme(None) self.theme_settings.enabled = False IStatusMessage(self.request).add(_(u"Theme disabled.")) self._setup() return True if 'form.button.AdvancedSave' in form: self.authorize() self.theme_settings.readNetwork = form.get('readNetwork', False) themeEnabled = form.get('themeEnabled', False) rules = form.get('rules', None) prefix = form.get('absolutePrefix', None) doctype = str(form.get('doctype', "")) hostnameBlacklist = form.get('hostnameBlacklist', []) parameterExpressions = {} parameterExpressionsList = form.get('parameterExpressions', []) for line in parameterExpressionsList: try: name, expression = line.split('=', 1) name = str(name.strip()) expression = str(expression.strip()) parameterExpressions[name] = expression except ValueError: message = _( 'error_invalid_parameter_expressions', default=u"Please ensure you enter one expression per " u"line, in the format <name> = <expression>.") self.errors['parameterExpressions'] = message themeBase = form.get('themeBase', None) markSpecialLinks = form.get('markSpecialLinks', None) extLinksOpenInNewWindow = form.get('extLinksOpenInNewWindow', None) if not self.errors: # Trigger onDisabled() on plugins if theme was active # previously and rules were changed if self.theme_settings.rules != rules: applyTheme(None) self.theme_settings.enabled = themeEnabled self.theme_settings.rules = rules self.theme_settings.absolutePrefix = prefix self.theme_settings.parameterExpressions = parameterExpressions self.theme_settings.hostnameBlacklist = hostnameBlacklist self.theme_settings.doctype = doctype # Theme base settings if themeBase is not None: self.pskin.default_skin = themeBase.encode('utf-8') if markSpecialLinks is not None: self.mark_special_links = markSpecialLinks if extLinksOpenInNewWindow is not None: self.ext_links_open_new_window = extLinksOpenInNewWindow IStatusMessage(self.request).add(_(u"Changes saved")) self._setup() return True else: IStatusMessage(self.request).add(_(u"There were errors"), 'error') self.redirectToFieldset('advanced') return False if 'form.button.Import' in form: self.authorize() enableNewTheme = form.get('enableNewTheme', False) replaceExisting = form.get('replaceExisting', False) themeArchive = form.get('themeArchive', None) themeZip = None performImport = False try: themeZip = zipfile.ZipFile(themeArchive) except ( zipfile.BadZipfile, zipfile.LargeZipFile, ): logger.exception("Could not read zip file") self.errors['themeArchive'] = _( 'error_invalid_zip', default=u"The uploaded file is not a valid Zip archive") if themeZip: try: themeData = extractThemeInfo(themeZip, checkRules=False) except ( ValueError, KeyError, ), e: logger.warn(str(e)) self.errors['themeArchive'] = _( 'error_no_rules_file', u"The uploaded file does not contain a valid theme " u"archive.") else: themeContainer = getOrCreatePersistentResourceDirectory() themeExists = themeData.__name__ in themeContainer if themeExists: if not replaceExisting: self.errors['themeArchive'] = _( 'error_already_installed', u"This theme is already installed. Select " u"'Replace existing theme' and re-upload to " u"replace it.") else: del themeContainer[themeData.__name__] performImport = True else: performImport = True if performImport: themeContainer.importZip(themeZip) themeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, themeData.__name__) if themeDirectory is not None: # If we don't have a rules file, use the template if themeData.rules == u"/++{0:s}++{1:s}/{2:s}".format( THEME_RESOURCE_NAME, themeData.__name__, RULE_FILENAME, ) and not themeDirectory.isFile(RULE_FILENAME): templateThemeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, TEMPLATE_THEME) themeDirectory.writeFile( RULE_FILENAME, templateThemeDirectory.readFile(RULE_FILENAME)) if not themeDirectory.isFile(DEFAULT_THEME_FILENAME): IStatusMessage(self.request).add( _(u"A boilerplate rules.xml was added to " u"your theme, but no index.html file " u"found. Update rules.xml to reference " u"the current theme file."), 'warning', ) plugins = getPlugins() pluginSettings = getPluginSettings(themeDirectory, plugins) if pluginSettings is not None: for name, plugin in plugins: plugin.onCreated(themeData.__name__, pluginSettings[name], pluginSettings) if enableNewTheme: applyTheme(themeData) self.theme_settings.enabled = True if not self.errors: self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( self.site_url, themeData.__name__)) return False else: IStatusMessage(self.request).add(_(u"There were errors"), "error") self.renderOverlay('upload') return True if 'form.button.CreateTheme' in form: self.authorize() title = form.get('title') description = form.get('description') or '' baseOn = form.get('baseOn', TEMPLATE_THEME) enableImmediately = form.get('enableImmediately', True) if not title: self.errors['title'] = _(u"Title is required") IStatusMessage(self.request).add(_(u"There were errors"), 'error') self.renderOverlay('new-theme') return True else: if any(x.__name__ == title for x in getZODBThemes()): self.errors['title'] = _(u"Duplicate title") IStatusMessage(self.request).add( _(u"This title is already in use"), 'error') return True name = createThemeFromTemplate(title, description, baseOn) self._setup() if enableImmediately: themeData = self.getThemeData(self.availableThemes, name) applyTheme(themeData) self.theme_settings.enabled = True self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( self.site_url, name)) return False if 'form.button.DeleteSelected' in form: self.authorize() toDelete = form.get('themes', []) themeDirectory = getOrCreatePersistentResourceDirectory() for theme in toDelete: del themeDirectory[theme] IStatusMessage(self.request).add(_(u"Theme deleted"), 'info') self._setup() return True return True
def cancel(self, action): IStatusMessage(self.request).addStatusMessage(_("Changes canceled."), type="info") self.request.response.redirect( '%s%s' % (self.request['ACTUAL_URL'], self.makeQuery()))
def handle_send(self, action): self.portal_state = getMultiAdapter((self.context, self.request), name=u'plone_portal_state') self.portal = self.portal_state.portal() self.membership_tool = getToolByName(self.context, 'portal_membership') self.feedback_template = self.context.restrictedTraverse( '@@author-feedback-template') data, errors = self.extractData() if errors: IStatusMessage(self.request).addStatusMessage( self.formErrorsMessage, type=u'error') return referer = data.get('referer', 'unknown referer') subject = data.get('subject', '') message = data.get('message', '') # Author is None means portal administrator author = data.get('author', None) sender = self.portal_state.member() registry = getUtility(IRegistry) mail_settings = registry.forInterface(IMailSchema, prefix='plone') envelope_from = mail_settings.email_from_address if author is None: send_to_address = mail_settings.email_from_address else: author_member = self.membership_tool.getMemberById(author) send_to_address = author_member.getProperty('email') send_from_address = sender.getProperty('email') if send_from_address == '': IStatusMessage(self.request).addStatusMessage( _(u'Could not find a valid email address'), type=u'error') return sender_id = "%s (%s), %s" % (sender.getProperty('fullname'), sender.getId(), send_from_address) mail_host = getUtility(IMailHost) registry = getUtility(IRegistry) email_charset = registry.get('plone.email_charset', 'utf-8') try: message = self.feedback_template( self, send_from_address=send_from_address, sender_id=sender_id, url=referer, subject=subject, message=message, encoding=email_charset, email_from_name=mail_settings.email_from_name) message = message.encode(email_charset) mail_host.send(message, send_to_address, envelope_from, subject=subject, charset=email_charset) except ConflictError: raise except Exception as e: logger.info("Unable to send mail: " + str(e)) IStatusMessage(self.request).addStatusMessage( _(u'Unable to send mail.'), type=u'error') return IStatusMessage(self.request).addStatusMessage(_(u'Mail sent.'), type=u'info') self.request.response.redirect( '%s/author/%s' % (self.portal.absolute_url(), author or '')) return
def handleApply(self, action): portal = getSite() pm = getToolByName(portal, 'portal_membership') lt = getToolByName(portal, 'portal_languages') userid = pm.getAuthenticatedMember() user_email = userid.getProperty('email', '') if not user_email: IStatusMessage(self.request).addStatusMessage( _(u'La reunió no es pot crear perquè l\'usuari no te informat la adreca de correu electrònic.' ), u'error') self.request.response.redirect(portal.absolute_url()) data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return str_start_date = data.get('start_date').isoformat().replace('T', '-')[:-6] str_invitats_convidats = data.get('invitats_convidats', '').replace(' ', '') # Guard because the invitats_espectadors field is not required if data.get('invitats_espectadors', False): str_invitats_espectadors = data.get('invitats_espectadors', '').replace(' ', '') else: str_invitats_espectadors = '' guests = data.get('invitats_convidats') session_load = len(guests.split(',')) payload = dict(servidor=BBB_SERVER, inici=str_start_date, durada=data.get('durada'), carrega=session_load, descripcio=data.get('nom_reunio'), owner=user_email, invite_rw=str_invitats_convidats, invite_ro=str_invitats_espectadors, lang=lt.getDefaultLanguage()) req = requests.post(BBB_ENDPOINT, data=payload) try: # Redirect back to the front page with a status message if int(req.text) > 0: IStatusMessage(self.request).addStatusMessage( _(u'La reunió virtual ha estat creada.'), u'info') else: IStatusMessage(self.request).addStatusMessage( _(u'Hi ha hagut algun problema i la reunió virtual no ha estat creada.' ), u'info') except: IStatusMessage(self.request).addStatusMessage( _(u'Hi ha hagut algun problema i la reunió virtual no ha estat creada.' ), u'info') self.request.response.redirect(portal.absolute_url())
def actionMigrate(self, action, data): output = migrateMonetEvent(self.context) IStatusMessage(self.request).addStatusMessage(output, type='info') return self.request.response.redirect(self.context.absolute_url())
def handleCancel(self, action): IStatusMessage(self.request).addStatusMessage(_(u"Changes canceled."), "info") self.request.response.redirect( "%s/%s" % (self.context.absolute_url(), self.control_panel_view))
def cancel(self, action): IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled.'), type='info') self.request.response.redirect(self.context.absolute_url()) return ''
fullname = row[0] + ' ' + row[1] username = self.sanitize(fullname.lower().replace(' ', '-')) properties['username'] = username properties['fullname'] = fullname try: # addMember() returns MemberData object member = regtool.addMember(username, 'secret', properties=properties) except ValueError, e: # Give user visual feedback what went wrong IStatusMessage(self.request).add(_(u"Could not create the users. %s" % username) + unicode(e), "error") continue else: log.info('Registered dummy user: %s' % fullname) row_num += 1 IStatusMessage(self.request).add(_(u"Succesfully created %d users." % (row_num-1)), "info") return self.request.RESPONSE.redirect('/'.join(self.context.getPhysicalPath())) def sanitize(self, str): for code, ascii in [('\xc3\xbc', 'ue'), ('\xc3\xb6', 'oe'), ('\xc3\xa4', 'ae'), ('\xc3\xa7', 'c'), ('\xc3\xa8', 'e'), ('\xc3\xa9', 'e'), ('\xc3\xab', 'e'), ('\xc3\xaf', 'i'), ('\xc5\x9e', 'S'), ('\xc5\x9f', 'e'), ]: str = str.replace(code, ascii)
def handleComment(self, action): context = aq_inner(self.context) # Check if conversation is enabled on this content object if not self.__parent__.restrictedTraverse( '@@conversation_view').enabled(): raise Unauthorized("Discussion is not enabled for this content " "object.") # Validation form data, errors = self.extractData() if errors: return # Validate Captcha registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings, check=False) portal_membership = getToolByName(self.context, 'portal_membership') captcha_enabled = settings.captcha != 'disabled' anonymous_comments = settings.anonymous_comments anon = portal_membership.isAnonymousUser() if captcha_enabled and anonymous_comments and anon: if not 'captcha' in data: data['captcha'] = u"" captcha = CaptchaValidator(self.context, self.request, None, ICaptcha['captcha'], None) captcha.validate(data['captcha']) # some attributes are not always set author_name = u"" # Create comment comment = createObject('plone.Comment') # Set comment mime type to current setting in the discussion registry comment.mime_type = settings.text_transform # Set comment attributes (including extended comment form attributes) for attribute in self.fields.keys(): setattr(comment, attribute, data[attribute]) # Make sure author_name is properly encoded if 'author_name' in data: author_name = data['author_name'] if isinstance(author_name, str): author_name = unicode(author_name, 'utf-8') # Set comment author properties for anonymous users or members can_reply = getSecurityManager().checkPermission( 'Reply to item', context) portal_membership = getToolByName(self.context, 'portal_membership') if anon and anonymous_comments: # Anonymous Users comment.author_name = author_name comment.author_email = u"" comment.user_notification = None comment.creation_date = datetime.utcnow() comment.modification_date = datetime.utcnow() elif not portal_membership.isAnonymousUser() and can_reply: # Member member = portal_membership.getAuthenticatedMember() username = member.getUserName() email = member.getProperty('email') fullname = member.getProperty('fullname') if not fullname or fullname == '': fullname = member.getUserName() # memberdata is stored as utf-8 encoded strings elif isinstance(fullname, str): fullname = unicode(fullname, 'utf-8') if email and isinstance(email, str): email = unicode(email, 'utf-8') comment.creator = username comment.author_username = username comment.author_name = fullname comment.author_email = email comment.creation_date = datetime.utcnow() comment.modification_date = datetime.utcnow() else: # pragma: no cover raise Unauthorized( u"Anonymous user tries to post a comment, but anonymous " u"commenting is disabled. Or user does not have the " u"'reply to item' permission.") # Add comment to conversation conversation = IConversation(self.__parent__) if data['in_reply_to']: # Add a reply to an existing comment conversation_to_reply_to = conversation.get(data['in_reply_to']) replies = IReplies(conversation_to_reply_to) comment_id = replies.addComment(comment) else: # Add a comment to the conversation comment_id = conversation.addComment(comment) # Redirect after form submit: # If a user posts a comment and moderation is enabled, a message is # shown to the user that his/her comment awaits moderation. If the user # has 'review comments' permission, he/she is redirected directly # to the comment. can_review = getSecurityManager().checkPermission( 'Review comments', context) workflowTool = getToolByName(context, 'portal_workflow') comment_review_state = workflowTool.getInfoFor(comment, 'review_state', None) if comment_review_state == 'pending' and not can_review: # Show info message when comment moderation is enabled IStatusMessage(self.context.REQUEST).addStatusMessage( _("Your comment awaits moderator approval."), type="info") self.request.response.redirect(self.action) else: # Redirect to comment (inside a content object page) self.request.response.redirect(self.action + '#' + str(comment_id))
def addStatusMessage(request, message, type='info'): """Add a status message to the request. """ IStatusMessage(request).addStatusMessage(message, type=type)
def __call__(self): state = self.request.form.get('state') messages = IStatusMessage(self.request) translator = self.context.translate navigation_root = api.portal.get_navigation_root(context=self.context) if 'error' in self.request.form: messages = IStatusMessage(self.request) reason = self.request.form.get('error') # noinspection PyArgumentList messages.add(translator( _(u'message_%s_failed' % state, mapping={u'reason': reason}), ), type='error') self.request.response.redirect(navigation_root.absolute_url() + '/@@id4me') return if 'code' not in self.request.form: raise BadRequest('no code given') code = self.request.form.get('code') if state == 'login': user = self.auth_util.verify_user_login(code) if user: acl_users = getToolByName(self.context, 'acl_users') # noinspection PyProtectedMember acl_users.session._setupSession(user.getId(), self.request.response) # noinspection PyArgumentList messages.add(translator(_(u'message_login_successful')), type='info') self.request.response.redirect(navigation_root.absolute_url()) else: # noinspection PyArgumentList messages.add(translator(_(u'message_no_user_connected')), type='error') self.request.response.redirect(navigation_root.absolute_url() + '/@@id4me') elif state == 'register': if not api.portal.get_registry_record( name='plone.enable_self_reg'): raise Forbidden() user = self.auth_util.register_user(code) acl_users = getToolByName(self.context, 'acl_users') # noinspection PyProtectedMember acl_users.session._setupSession(user.getId(), self.request.response) # noinspection PyArgumentList messages.add(translator( _(u'message_account_created', mapping={u'user_id': user.getId()})), type='info') self.request.response.redirect(navigation_root.absolute_url()) elif state == 'connect': if api.user.is_anonymous(): raise Forbidden('No user logged in') user = api.user.get_current() self.auth_util.connect_user_login(user=user, code=code) # noinspection PyArgumentList messages.add( translator( _( u'message_login_connected', # ToDo: get Identity Agent information mapping={u'agent': 'NOT_FOUND'})), type='info') self.request.response.redirect(navigation_root.absolute_url()) else: # ToDo: handle Case raise BadRequest('no state given')
def handleCancel(self, action): IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled'), 'info') self.request.response.redirect('{context_url}/{view}'.format( context_url=self.context.absolute_url(), view="@@overview-controlpanel"))
def redirect(self, message): IStatusMessage(self.request).add(message) self.request.response.redirect("{0:s}/@@theming-controlpanel".format( self.portalUrl))
def extract_attachments(self, positions, delete_action): dossier = self.find_parent_dossier() attachments_to_extract = filter( lambda att: att.get('position') in positions, get_attachments(self.context.msg)) # create documents from the selected attachments for att in attachments_to_extract: pos = att.get('position') filename = att.get('filename') # remove line breaks from the filename filename = re.sub('\s{1,}', ' ', filename) kwargs = { 'title': filename[:filename.rfind('.')].decode('utf-8'), 'file': self.get_attachment_as_namedfile(pos), 'keywords': (), 'digitally_available': True } doc = createContentInContainer(dossier, 'opengever.document.document', **kwargs) for schemata in iterSchemata(doc): for name, field in getFieldsInOrder(schemata): if name not in kwargs.keys(): default = queryMultiAdapter( ( doc, doc.REQUEST, # request None, # form field, None, # Widget ), IValue, name='default') if default is not None: default = default.get() if default is None: default = getattr(field, 'default', None) if default is None: try: default = field.missing_value except: pass field.set(field.interface(doc), default) # add a reference from the attachment to the mail intids = getUtility(IIntIds) iid = intids.getId(self.context) # prevent circular dependencies from opengever.document.behaviors import IRelatedDocuments IRelatedDocuments(doc).relatedItems = [RelationValue(iid)] msg = _(u'info_extracted_document', default=u'Created document ${title}', mapping={'title': doc.Title().decode('utf-8')}) IStatusMessage(self.request).addStatusMessage(msg, type='info') # reindex the new document to index also all the default values doc.reindexObject() # delete the attachments from the email message, if needed if delete_action in ('all', 'selected'): if delete_action == 'selected': pos_to_delete = positions else: # all pos_to_delete = [ int(att['position']) for att in get_attachments(self.context.msg) ] # set the new message file msg = remove_attachments(self.context.msg, pos_to_delete) self.context.message = NamedFile( data=msg.as_string(), contentType=self.context.message.contentType, filename=self.context.message.filename)
def handleSubmit(self, action): """ Here we should check couple of things: - If the token provided is valid. - If the signature contains the user data needed (username and hash made of his data are valid). If all is well and valid, we sudo login the user given. """ if not action.title == _(u'Verify'): return logger.debug('verify') data, errors = self.extractData() if errors: return False token = data.get('token', '') if not token: IStatusMessage(self.request).addStatusMessage( _("No token provided!"), 'error') return user = None username = self.request.get('auth_user', '') if username: user = api.user.get(username=username) # Validating the signed request data. If invalid (likely throttled with or expired), generate an # appropriate error message. user_data_validation_result = validate_user_data( request=self.request, user=user) if not user_data_validation_result.result: if 'Signature timestamp expired!' in user_data_validation_result.reason: # Remove used authentication code user.setMemberProperties( mapping={ 'mobile_number_authentication_code': '', }) IStatusMessage(self.request).addStatusMessage( _("Invalid data. Details: {0}").format(' '.join( user_data_validation_result.reason)), 'error') return valid_token = validate_mobile_number_authentication_code(token, user=user) if valid_token: # We should login the user here self.context.acl_users.session._setupSession( str(username), self.context.REQUEST.RESPONSE) mapping = { 'mobile_number_authentication_code': '', } mapping.update(get_updated_ips_for_member_properties_update(user)) # Remove used authentication code and update the IPs list user.setMemberProperties(mapping=mapping) # TODO: Is there a nicer way of resolving the "@@sms_authenticator_token_form" URL? IStatusMessage(self.request).addStatusMessage( __("Welcome! You are now logged in."), 'info') request_data = extract_request_data(self.request) redirect_url = request_data.get('next_url', self.context.absolute_url()) self.request.response.redirect(redirect_url) else: IStatusMessage(self.request).addStatusMessage( _("Invalid token or token expired."), 'error')
def handleSubmit(self, action): data, errors = self.extractData() if errors: return False username = data.get('username', '') mobile_number = data.get('mobile_number', '') user = api.user.get(username=username) reason = None if user: try: # Here we need to generate a token which is valid for let's say, 2 hours # using which it should be possible to reset the mobile number. The `signature` # generated should be saved in the user profile `mobile_number_reset_token`. ska_secret_key = get_ska_secret_key(request=self.request, user=user) # We also need to generate another token (no security, just a random string) # to sent to users' mobile number. mobile_number_reset_code = generate_code(user) token_lifetime = get_ska_token_lifetime() signature = Signature.generate_signature( auth_user=username, secret_key=ska_secret_key, lifetime=token_lifetime, extra={'mobile_number': mobile_number}) request_helper = RequestHelper(signature_param='signature', auth_user_param='auth_user', valid_until_param='valid_until') signed_url = request_helper.signature_to_url( signature=signature, endpoint_url='{0}/{1}'.format(self.context.absolute_url(), '@@reset-mobile-number')) # Send the SMS sms_sent = send_mobile_number_reset_confirmation_code_sms( mobile_number=mobile_number, code=mobile_number_reset_code) if not sms_sent: IStatusMessage(self.request).addStatusMessage( _("An error occured while sending the SMS to the number specified." ), 'info') redirect_url = "{0}".format(self.context.absolute_url()) self.request.response.redirect(redirect_url) return # Save the `signature` value to the `mobile_number_reset_token`. user.setMemberProperties( mapping={ 'mobile_number_reset_token': str(signature), 'mobile_number_reset_code': mobile_number_reset_code, 'mobile_number_authentication_code': '', }) # Now we need to send an email to user with URL in and a small explanations. try: host = getToolByName(self, 'MailHost') mail_text_template = self.context.restrictedTraverse( 'request_mobile_number_reset_email') mail_text = mail_text_template( member=user, mobile_number_reset_url=signed_url, charset='utf-8') mail_text = mail_text.format( mobile_number_reset_url=signed_url) host.send(mail_text.encode('UTF-8'), immediate=True, msg_type='text/html') except SMTPRecipientsRefused as e: raise SMTPRecipientsRefused( 'Recipient address rejected by server') IStatusMessage(self.request).addStatusMessage( _("An email with further instructions for (re)setting your mobile number has been sent." ), 'info') redirect_url = "{0}/@@reset-email-sent".format( self.context.absolute_url()) self.request.response.redirect(redirect_url) except ValueError as e: reason = _(str(e)) else: reason = _("Invalid username.") if reason is not None: IStatusMessage(self.request).addStatusMessage( _("Request for mobile number reset is failed! {0}").format( reason), 'error')
def update(self): messages = IStatusMessage(self.request).show() for message in messages: message.type = self.mapping.get(message.type, message.type) self.messages = messages