def flash_if_unregistered(event, get_person_links): """Flash message when adding users with no fossir account :param event: Current event :param get_person_links: Callable returning list of person links to check """ old_non_users = {pl for pl in get_person_links() if pl.person.user is None} yield new_non_users = {pl for pl in get_person_links() if pl.person.user is None} added_non_users = len(new_non_users - old_non_users) if not added_non_users: return warning = ngettext('You have added a user with no fossir account.', 'You have added {} users with no fossir account.', added_non_users).format(added_non_users) msg = _( 'An fossir account may be needed to upload materials and/or manage contents.' ) if event.can_manage(session.user): continue_msg = (escape( _('To invite them, please go to the {link_start}Roles page{link_end}' )).format(link_start=Markup('<a href="{}">').format( url_for('persons.person_list', event)), link_end=Markup('</a>'))) else: continue_msg = ngettext( 'Please contact the manager if you think this user should be able to access these ' 'features.', 'Please contact the manager if you think these users should be able to access ' 'these features.', added_non_users) flash(Markup(' ').join([warning, msg, continue_msg]), 'warning')
def _process(self): tpl = get_template_module( 'events/registration/emails/invitation_default.html', event=self.event) form_cls = InvitationFormExisting if request.args.get( 'existing') == '1' else InvitationFormNew defaults = FormDefaults(email_body=tpl.get_html_body(), email_subject=tpl.get_subject()) form = form_cls(obj=defaults, regform=self.regform) skip_moderation = form.skip_moderation.data if 'skip_moderation' in form else False if form.validate_on_submit(): for user in form.users.data: self._create_invitation(user, skip_moderation, form.email_from.data, form.email_subject.data, form.email_body.data) num = len(form.users.data) flash( ngettext("The invitation has been sent.", "{n} invitations have been sent.", num).format(n=num), 'success') return jsonify_data( invitation_list=_render_invitation_list(self.regform)) return jsonify_template( 'events/registration/management/regform_invite.html', regform=self.regform, form=form)
def _process(self): cfp = self.event.cfp form_data = { 'managers': cfp.managers, 'judges': cfp.judges, 'content_reviewers': cfp.content_reviewers, 'layout_reviewers': cfp.layout_reviewers } form = PaperTeamsForm(event=self.event, **form_data) if form.validate_on_submit(): teams = { 'managers': form.managers.data, 'judges': form.judges.data } if cfp.content_reviewing_enabled: teams['content_reviewers'] = form.content_reviewers.data if cfp.layout_reviewing_enabled: teams['layout_reviewers'] = form.layout_reviewers.data unassigned_contribs = update_team_members(self.event, **teams) flash(_("The members of the teams were updated successfully"), 'success') if unassigned_contribs: flash( ngettext("Users have been removed from 1 contribution", "Users have been removed from {} contributions", len(unassigned_contribs)).format( len(unassigned_contribs)), 'warning') return jsonify_data() return jsonify_template('events/papers/management/teams.html', form=form)
def _associate_registrations(user, **kwargs): from fossir.modules.events.registration.models.registrations import Registration reg_alias = db.aliased(Registration) subquery = db.session.query(reg_alias).filter( reg_alias.user_id == user.id, reg_alias.registration_form_id == Registration.registration_form_id, ~reg_alias.is_deleted) registrations = ( Registration.find( Registration.user_id == None, # noqa Registration.email.in_(user.all_emails), ~subquery.exists(), ~Registration.is_deleted).order_by( Registration.submitted_dt.desc()).all()) if not registrations: return done = set() for registration in registrations: if registration.registration_form_id in done: continue logger.info('Associating %s with %s', registration, user) registration.user = user done.add(registration.registration_form_id) db.session.flush() num = len(done) flash( ngettext("A registration has been linked to your account.", "{n} registrations have been linked to your account.", num).format(n=num), 'info')
def _convert_email_principals(user, **kwargs): from fossir.modules.events.sessions.models.principals import SessionPrincipal sessions = SessionPrincipal.replace_email_with_user(user, 'session') if sessions: num = len(sessions) flash(ngettext("You have been granted manager/coordination privileges for a session.", "You have been granted manager/coordination privileges for {} sessions.", num).format(num), 'info')
def _process(self): for contrib in self.contribs: delete_contribution(contrib) deleted_count = len(self.contribs) flash( ngettext("The contribution has been deleted.", "{count} contributions have been deleted.", deleted_count).format(count=deleted_count), 'success') return jsonify_data(**self.list_generator.render_list())
def format_human_timedelta(delta, granularity='seconds', narrow=False): """Formats a timedelta in a human-readable way :param delta: the timedelta to format :param granularity: the granularity, i.e. the lowest unit that is still displayed. when set e.g. to 'minutes', the output will never contain seconds unless the whole timedelta spans less than a minute. Accepted values are 'seconds', 'minutes', 'hours' and 'days'. :param narrow: if true, only the short unit names will be used """ field_order = ('days', 'hours', 'minutes', 'seconds') long_names = { 'seconds': lambda n: ngettext(u'{0} second', u'{0} seconds', n).format(n), 'minutes': lambda n: ngettext(u'{0} minute', u'{0} minutes', n).format(n), 'hours': lambda n: ngettext(u'{0} hour', u'{0} hours', n).format(n), 'days': lambda n: ngettext(u'{0} day', u'{0} days', n).format(n), } short_names = { 'seconds': lambda n: ngettext(u'{0}s', u'{0}s', n).format(n), 'minutes': lambda n: ngettext(u'{0}m', u'{0}m', n).format(n), 'hours': lambda n: ngettext(u'{0}h', u'{0}h', n).format(n), 'days': lambda n: ngettext(u'{0}d', u'{0}d', n).format(n), } if narrow: long_names = short_names values = OrderedDict((key, 0) for key in field_order) values['seconds'] = delta.total_seconds() values['days'], values['seconds'] = divmod(values['seconds'], 86400) values['hours'], values['seconds'] = divmod(values['seconds'], 3600) values['minutes'], values['seconds'] = divmod(values['seconds'], 60) for key, value in values.iteritems(): values[key] = int(value) # keep all fields covered by the granularity, and if that results in # no non-zero fields, include otherwise excluded ones used_fields = set(field_order[:field_order.index(granularity) + 1]) available_fields = [x for x in field_order if x not in used_fields] used_fields -= {k for k, v in values.iteritems() if not v} while not sum(values[x] for x in used_fields) and available_fields: used_fields.add(available_fields.pop(0)) for key in available_fields: values[key] = 0 nonzero = OrderedDict((k, v) for k, v in values.iteritems() if v) if not nonzero: return long_names[granularity](0) elif len(nonzero) == 1: key, value = nonzero.items()[0] return long_names[key](value) else: parts = [short_names[key](value) for key, value in nonzero.iteritems()] return u' '.join(parts)
def _convert_email_principals(user, **kwargs): from fossir.modules.events.models.principals import EventPrincipal events = EventPrincipal.replace_email_with_user(user, 'event') if events: num = len(events) flash( ngettext( "You have been granted manager/submission privileges for an event.", "You have been granted manager/submission privileges for {} events.", num).format(num), 'info')
def _process(self): for event in self.events: event.move(self.target_category) flash( ngettext('You have moved one event to the category "{cat}"', 'You have moved {count} events to the category "{cat}"', len(self.events)).format(count=len(self.events), cat=self.target_category.title), 'success') return jsonify_data(flash=False)
def _process(self): for subcategory in self.subcategories: move_category(subcategory, self.target_category) flash( ngettext('{0} category moved to "{1}".', '{0} categories moved to "{1}".', len(self.subcategories)).format( len(self.subcategories), self.target_category.title), 'success') return jsonify_data(flash=False)
def validators(self): min_choices = self.object.field_data.get('min_choices') max_choices = self.object.field_data.get('max_choices') if min_choices is None and max_choices is None: return if min_choices is None: min_choices = -1 if max_choices is None: max_choices = -1 if max_choices == -1: message = ngettext('Please select at least %(min)d option.', 'Please select at least %(min)d options.', min_choices) elif min_choices == -1: message = ngettext('Please select no more than %(max)d option.', 'Please select no more than %(max)d options.', max_choices) else: message = _('Please select between %(min)d and %(max)d options.') return [Length(min=min_choices, max=max_choices, message=message)]
def _convert_email_principals(user, **kwargs): from fossir.modules.events.contributions.models.principals import ContributionPrincipal contributions = ContributionPrincipal.replace_email_with_user( user, 'contribution') if contributions: num = len(contributions) flash( ngettext( "You have been granted manager/submission privileges for a contribution.", "You have been granted manager/submission privileges for {} contributions.", num).format(num), 'info')
def _process(self): for registration in self.registrations: registration.is_deleted = True signals.event.registration_deleted.send(registration) logger.info('Registration %s deleted by %s', registration, session.user) self.event.log(EventLogRealm.management, EventLogKind.negative, 'Registration', 'Registration deleted: {}'.format(registration.full_name), session.user, data={'Email': registration.email}) num_reg_deleted = len(self.registrations) flash(ngettext("Registration was deleted.", "{num} registrations were deleted.", num_reg_deleted).format(num=num_reg_deleted), 'success') return jsonify_data()
def _process(self): form = BulkAbstractJudgmentForm( event=self.event, abstract_id=[a.id for a in self.abstracts], judgment=request.form.get('judgment')) if form.validate_on_submit(): judgment_data, abstract_data = form.split_data submitted_abstracts = { abstract for abstract in self.abstracts if abstract.state == AbstractState.submitted } for abstract in submitted_abstracts: judge_abstract(abstract, abstract_data, judge=session.user, **judgment_data) num_judged_abstracts = len(submitted_abstracts) num_prejudged_abstracts = len( self.abstracts) - num_judged_abstracts if num_judged_abstracts: flash( ngettext( "One abstract has been judged.", "{num} abstracts have been judged.", num_judged_abstracts).format(num=num_judged_abstracts), 'success') if num_prejudged_abstracts: flash( ngettext( "One abstract has been skipped since it is already judged.", "{num} abstracts have been skipped since they are already judged.", num_prejudged_abstracts).format( num=num_prejudged_abstracts), 'warning') return jsonify_data(**self.list_generator.render_list()) return jsonify_form(form=form, fields=form._order, submit=_('Judge'), disabled_until_change=False)
def _process(self): delete_contribs = request.values.get('delete_contribs') == '1' deleted_contrib_count = 0 for abstract in self.abstracts: if delete_contribs and abstract.contribution: deleted_contrib_count += 1 delete_abstract(abstract, delete_contribs) deleted_abstract_count = len(self.abstracts) flash( ngettext( "The abstract has been deleted.", "{count} abstracts have been deleted.", deleted_abstract_count).format(count=deleted_abstract_count), 'success') if deleted_contrib_count: flash( ngettext( "The linked contribution has been deleted.", "{count} linked contributions have been deleted.", deleted_contrib_count).format(count=deleted_contrib_count), 'success') return jsonify_data(**self.list_generator.render_list())
def _process(self): is_submitted = 'confirmed' in request.form if not is_submitted: return jsonify_template('events/management/delete_events.html', events=self.events) for ev in self.events[:]: ev.delete('Bulk-deleted by category manager', session.user) flash( ngettext('You have deleted one event', 'You have deleted {} events', len(self.events)).format(len(self.events)), 'success') return jsonify_data(flash=False, redirect=url_for('.manage_content', self.category))
def _process(self): count = 0 for sess in self.event.sessions: for convener in sess.conveners: principal = convener.person.principal if principal: sess.update_principal(principal, full_access=True) count += 1 self.event.log(EventLogRealm.management, EventLogKind.positive, 'Protection', 'Modification rights have been granted to all session conveners', session.user) flash(ngettext('Session modification rights have been granted to one session convener', 'Session modification rights have been granted to {} session conveners', count).format(count)) return redirect(url_for('.person_list', self.event))
def _process(self): form = BulkPaperJudgmentForm( event=self.event, judgment=request.form.get('judgment'), contribution_id=[c.id for c in self.contributions]) if form.validate_on_submit(): submitted_papers = [ c.paper for c in self.contributions if c.paper and c.paper.last_revision.state == PaperRevisionState.submitted ] for submitted_paper in submitted_papers: judge_paper(submitted_paper, form.judgment.data, form.judgment_comment.data, judge=session.user) num_submitted_papers = len(submitted_papers) num_not_submitted_papers = len( self.contributions) - num_submitted_papers if num_submitted_papers: flash( ngettext( "One paper has been judged.", "{num} papers have been judged.", num_submitted_papers).format(num=num_submitted_papers), 'success') if num_not_submitted_papers: flash( ngettext( "One contribution has been skipped since it has no paper submitted yet or it is in " "a final state.", "{num} contributions have been skipped since they have no paper submitted yet or they " "are in a final state.", num_not_submitted_papers).format( num=num_not_submitted_papers), 'warning') return jsonify_data(**self.list_generator.render_list()) return jsonify_form(form=form, submit=_('Judge'), disabled_until_change=False)
def _process(self): tpl = get_template_module( 'events/surveys/emails/survey_link_email.html', event=self.event) form = InvitationForm(body=tpl.get_html_body(), subject=tpl.get_subject(), event=self.event) if form.validate_on_submit(): self._send_emails(form, form.recipients.data) num = len(form.recipients.data) flash( ngettext('Your email has been sent.', '{} emails have been sent.', num).format(num)) return jsonify_data(flash=True) return jsonify_form(form)
def _process(self): count = 0 for cont in self.event.contributions: for entry in set(cont.acl_entries): cont.update_principal(entry.principal, del_roles={'submit'}) count += 1 for entry in set(self.event.acl_entries): self.event.update_principal(entry.principal, del_roles={'submit'}) count += 1 self.event.log(EventLogRealm.management, EventLogKind.negative, 'Protection', 'Submission privileges have been revoked from event submitters', session.user) flash(ngettext('Submission rights have been revoked from one user', 'Submission rights have been revoked from {} users', count).format(count)) return redirect(url_for('.person_list', self.event))
def _process(self): tpl = get_template_module('events/registration/emails/custom_email_default.html') default_body = tpl.get_html_body() registration_ids = request.form.getlist('registration_id') form = EmailRegistrantsForm(body=default_body, regform=self.regform, registration_id=registration_ids, recipients=[x.email for x in self.registrations]) if not self.regform.tickets_enabled: del form.attach_ticket if form.validate_on_submit(): self._send_emails(form) num_emails_sent = len(self.registrations) flash(ngettext("The email was sent.", "{num} emails were sent.", num_emails_sent).format(num=num_emails_sent), 'success') return jsonify_data() return jsonify_template('events/registration/management/email.html', form=form, regform=self.regform)
def _process(self): count = 0 for cont in self.event.contributions: speakers = cont.speakers[:] for subcontrib in cont.subcontributions: speakers += subcontrib.speakers for speaker in speakers: principal = speaker.person.principal if principal: cont.update_principal(principal, add_roles={'submit'}) count += 1 self.event.log(EventLogRealm.management, EventLogKind.positive, 'Protection', 'Contribution speakers have been granted with submission rights', session.user) flash(ngettext('Submission rights have been granted to one speaker', 'Submission rights have been granted to {} speakers', count).format(count)) return redirect(url_for('.person_list', self.event))
def _event_type_changed(event, **kwargs): from fossir.modules.events.features.util import (get_enabled_features, get_disallowed_features, set_feature_enabled, format_feature_names) conflicting = get_enabled_features( event, only_explicit=True) & get_disallowed_features(event) if conflicting: for feature in conflicting: set_feature_enabled(event, feature, False) if request.endpoint != 'api.jsonrpc': # XXX: we cannot flash a message in the legacy js ajax editor for the event type. # remove this check once we don't use it anymore (on the general settings page) flash( ngettext( 'Feature disabled: {features} (not available for this event type)', 'Features disabled: {features} (not available for this event type)', len(conflicting)).format( features=format_feature_names(conflicting)), 'warning')
def _process(self): person_ids = request.form.getlist('person_id') user_ids = request.form.getlist('user_id') recipients = set(self._find_event_persons(person_ids, request.args.get('not_invited_only') == '1')) recipients |= set(self._find_users(user_ids)) if self.no_account: tpl = get_template_module('events/persons/emails/invitation.html', event=self.event) disabled_until_change = False else: tpl = get_template_module('events/persons/emails/generic.html', event=self.event) disabled_until_change = True form = EmailEventPersonsForm(person_id=person_ids, user_id=user_ids, recipients=[x.email for x in recipients], body=tpl.get_html_body(), subject=tpl.get_subject(), register_link=self.no_account, event=self.event) if form.validate_on_submit(): self._send_emails(form, recipients) num = len(recipients) flash(ngettext('Your email has been sent.', '{} emails have been sent.', num).format(num)) return jsonify_data() return jsonify_template('events/persons/email_dialog.html', form=form, disabled_until_change=disabled_until_change)
def _process_DELETE(self): prev = get_enabled_features(self.event) feature = get_feature_definition(request.view_args['feature']) changed = set() if set_feature_enabled(self.event, feature.name, False): current = get_enabled_features(self.event) changed = prev - current flash( ngettext('Feature disabled: {features}', 'Features disabled: {features}', len(changed)).format( features=format_feature_names(changed)), 'warning') logger.info("Feature '%s' for event %s disabled by %s", feature.name, self.event, session.user) self.event.log(EventLogRealm.management, EventLogKind.negative, 'Features', 'Disabled {}'.format(feature.friendly_name), session.user) return jsonify_data(enabled=False, event_menu=self.render_event_menu(), changed=list(changed))
def _process_PUT(self): prev = get_enabled_features(self.event) feature = get_feature_definition(request.view_args['feature']) if feature.name in get_disallowed_features(self.event): raise Forbidden('Feature not available') changed = set() if set_feature_enabled(self.event, feature.name, True): current = get_enabled_features(self.event) changed = current - prev flash( ngettext('Feature enabled: {features}', 'Features enabled: {features}', len(changed)).format( features=format_feature_names(changed)), 'success') logger.info("Feature '%s' for event %s enabled by %s", feature.name, self.event, session.user) self.event.log(EventLogRealm.management, EventLogKind.positive, 'Features', 'Enabled {}'.format(feature.friendly_name), session.user) return jsonify_data(enabled=True, event_menu=self.render_event_menu(), changed=list(changed))
def _process(self): files = request.files.getlist('image') num = 0 for f in files: filename = secure_filename(f.filename, 'image') data = BytesIO() shutil.copyfileobj(f, data) data.seek(0) try: image_type = Image.open(data).format.lower() except IOError: # Invalid image data continue data.seek(0) # XXX: mpo is basically JPEG and JPEGs from some cameras are (wrongfully) detected as mpo if image_type == 'mpo': image_type = 'jpeg' elif image_type not in {'jpeg', 'gif', 'png'}: flash( _("The image '{name}' has an invalid type ({type}); only JPG, GIF and PNG are allowed." ).format(name=f.filename, type=image_type), 'error') continue content_type = 'image/' + image_type image = ImageFile(event=self.event, filename=filename, content_type=content_type) image.save(data) num += 1 db.session.flush() logger.info('Image %s uploaded by %s', image, session.user) signals.event_management.image_created.send(image, user=session.user) flash( ngettext("The image has been uploaded", "{count} images have been uploaded", num).format(count=len(files)), 'success') return jsonify_data(image_list=_render_image_list(self.event))
def _process(self): form = AddAttachmentFilesForm(linked_object=self.object) if form.validate_on_submit(): files = form.files.data folder = form.folder.data or AttachmentFolder.get_or_create_default(linked_object=self.object) for f in files: filename = secure_filename(f.filename, 'attachment') attachment = Attachment(folder=folder, user=session.user, title=to_unicode(f.filename), type=AttachmentType.file, protection_mode=form.protection_mode.data) if attachment.is_self_protected: attachment.acl = form.acl.data content_type = mimetypes.guess_type(f.filename)[0] or f.mimetype or 'application/octet-stream' attachment.file = AttachmentFile(user=session.user, filename=filename, content_type=content_type) attachment.file.save(f.stream) db.session.add(attachment) db.session.flush() logger.info('Attachment %s uploaded by %s', attachment, session.user) signals.attachments.attachment_created.send(attachment, user=session.user) flash(ngettext("The attachment has been uploaded", "{count} attachments have been uploaded", len(files)) .format(count=len(files)), 'success') return jsonify_data(attachment_list=_render_attachment_list(self.object)) return jsonify_template('attachments/upload.html', form=form, action=url_for('.upload', self.object), protection_message=_render_protection_message(self.object), folders_protection_info=_get_folders_protection_info(self.object))
def test_ngettext(): session.lang = 'fr_MP' assert ngettext(u'{} cow', u'{} cows', 1).format(1) == u'1 vache' assert ungettext(u'{} cow', u'{} cows', 42).format(42) == u'42 vaches'