def render_registration_fields(self, req, data, fragments): self.log.debug("Adding registration check data fields") if self.replace_checks: for check in self.listeners: try: if check.__class__.__name__ != 'RegistrationFilterAdapter': self.log.debug("Add registration check data %s", check) fragment, f_data = \ check.render_registration_fields(req, data) try: fragments['optional'] = \ tag(fragments.get('optional', ''), fragment.get('optional', '')) fragments['required'] = \ tag(fragments.get('required', ''), fragment.get('required', '')) except AttributeError: if fragment is not None and fragment != '': fragments['required'] = \ tag(fragments.get('required', ''), fragment) data.update(f_data) except Exception, e: self.log.exception("Adding registration fields failed: %s", e)
def render_registration_fields(self, req, data): """Add a hidden text input field to the registration form, and a visible one with mandatory input as well, if token is configured. """ if self.reg_basic_token: # Preserve last input for editing on failure instead of typing # everything again. old_value = req.args.get('basic_token', '') if self.reg_basic_question: # TRANSLATOR: Question-style hint for visible bot trap # registration input field. hint = tag.p(_("Please answer above: %(question)s", question=self.reg_basic_question), class_='hint') else: # TRANSLATOR: Verbatim token hint for visible bot trap # registration input field. hint = tag.p(tag_( "Please type [%(token)s] as verification token, " "exactly replicating everything within the braces.", token=tag.b(self.reg_basic_token)), class_='hint') insert = tag( tag.label(_("Parole:"), tag.input(type='text', name='basic_token', size=20, class_='textwidget', value=old_value)), hint) else: insert = None # TRANSLATOR: Registration form hint for hidden bot trap input field. insert = tag(insert, tag.input(type='hidden', name='sentinel', title=_("Better do not fill this field."))) return insert, data
def render_registration_fields(self, req, data): """Add an email address text input field to the registration form.""" # Preserve last input for editing on failure instead of typing # everything again. old_value = req.args.get('email', '').strip() insert = tag.label(_("Email:"), tag.input(type='text', name='email', size=20, class_='textwidget', value=old_value)) # Deferred import required to aviod circular import dependencies. from acct_mgr.web_ui import AccountModule reset_password = AccountModule(self.env).reset_password_enabled verify_account = self.env.is_enabled(EmailVerificationModule) and \ EmailVerificationModule(self.env).verify_email if verify_account: # TRANSLATOR: Registration form hints for a mandatory input field. hint = tag.p(_(""" The email address is required for Trac to send you a verification token. """), class_='hint') if reset_password: hint = tag(hint, tag.p(_(""" Entering your email address will also enable you to reset your password if you ever forget it. """), class_='hint')) return tag(insert, hint), data elif reset_password: # TRANSLATOR: Registration form hint, if email input is optional. hint = tag.p(_("""Entering your email address will enable you to reset your password if you ever forget it."""), class_='hint') return dict(optional=tag(insert, hint)), data else: # Always return the email text input itself as optional field. return dict(optional=insert), data
def validate_registration(self, req): if req.path_info == '/prefs': return acctmgr = AccountManager(self.env) username = acctmgr.handle_username_casing( req.args.get('username', '').strip()) if not username: raise RegistrationError(N_("Username cannot be empty.")) # Always exclude some special characters, i.e. # ':' can't be used in HtPasswdStore # '[' and ']' can't be used in SvnServePasswordStore blacklist = acctmgr.username_char_blacklist if contains_any(username, blacklist): pretty_blacklist = '' for c in blacklist: if pretty_blacklist == '': pretty_blacklist = tag(' \'', tag.b(c), '\'') else: pretty_blacklist = tag(pretty_blacklist, ', \'', tag.b(c), '\'') raise RegistrationError(N_( "The username must not contain any of these characters: %s"), tag.b(pretty_blacklist) ) # All upper-cased names are reserved for permission action names. if username.isupper(): raise RegistrationError(N_("A username with only upper-cased " "characters is not allowed.")) # Prohibit some user names, that are important for Trac and therefor # reserved, even if not in the permission store for some reason. if username.lower() in ['anonymous', 'authenticated']: raise RegistrationError(N_("Username %s is not allowed."), tag.b(username)) # NOTE: A user may exist in a password store but not in the permission # store. I.e. this happens, when the user (from the password store) # never logged in into Trac. So we have to perform this test here # and cannot just check for the user being in the permission store. # And better obfuscate whether an existing user or group name # was responsible for rejection of this user name. for store_user in acctmgr.get_users(): # Do it carefully by disregarding case. if store_user.lower() == username.lower(): raise RegistrationError(tag_( "Another account or group already exists, who's name " "differs from %(username)s only by case or is identical.", username=tag.b(username))) # Password consistency checks follow. password = req.args.get('password') if not password: raise RegistrationError(N_("Password cannot be empty.")) elif password != req.args.get('password_confirm'): raise RegistrationError(N_("The passwords must match."))
def render(context, field, event): if event[0] == 'test&1': if field == 'url': return 'http://example.org/path?foo=bar&baz=1' if field == 'summary': return 'summary 1: <b>&</b>' if field == 'description': return tag(tag.h1('Title 1st'), tag.p('body & < >')) if event[0] == 'test&2': if field == 'url': return 'http://example.org/path?baz=2&foo=bar' if field == 'summary': return tag('summary 2: ', tag.b('&')) if field == 'description': return tag(tag.h1('Title 2nd'), tag.p('body & < >'))
def _do_update(self, req): """Update component enable state.""" components = req.args.getlist('component') enabled = req.args.getlist('enable') added, removed = [], [] # FIXME: this needs to be more intelligent and minimize multiple # component names to prefix rules for component in components: is_enabled = bool(self.env.is_component_enabled(component)) must_enable = component in enabled if is_enabled != must_enable: self.config.set('components', component, 'disabled' if is_enabled else 'enabled') self.log.info("%sabling component %s", "Dis" if is_enabled else "En", component) if must_enable: added.append(component) else: removed.append(component) if added or removed: def make_list(items): parts = [item.rsplit('.', 1) for item in items] return tag.table(tag.tbody( tag.tr(tag.td(c, class_='trac-name'), tag.td('(%s.*)' % m, class_='trac-name')) for m, c in parts), class_='trac-pluglist') added.sort() removed.sort() notices = [] if removed: msg = ngettext("The following component has been disabled:", "The following components have been disabled:", len(removed)) notices.append(tag(msg, make_list(removed))) if added: msg = ngettext("The following component has been enabled:", "The following components have been enabled:", len(added)) notices.append(tag(msg, make_list(added))) # set the default value of options for only the enabled components for component in added: self.config.set_defaults(component=component) _save_config(self.config, req, self.log, notices)
def _link_crashes_by_id(self, req, ids): items = [] for i, word in enumerate(re.split(r'([;,\s]+)', ids)): if i % 2: items.append(word) elif word: crashid = word word = 'CrashId#%s' % word try: crash = CrashDump(env=self.env, id=crashid) word = \ tag.a( 'CrashId#%i' % crash.id, class_=crash['status'], href=req.href('crash', crash.uuid), title=crash.uuid ) except ResourceNotFound: pass items.append(word) if items: return tag(items) else: return None
def generate_captcha(self, req): session_id = "%d-3.4.0.001" % random.randint(1, 10000000) sign1 = hashlib.md5(session_id + req.remote_addr + self.private_key).hexdigest() sign2 = hashlib.md5(session_id + self.private_key).hexdigest() varblock = "var s_s_c_user_id = '%s';\n" % self.user_id varblock += "var s_s_c_session_id = '%s';\n" % session_id varblock += "var s_s_c_captcha_field_id = 'keycaptcha_response_field';\n" varblock += "var s_s_c_submit_button_id = 'keycaptcha_response_button';\n" varblock += "var s_s_c_web_server_sign = '%s';\n" % sign1 varblock += "var s_s_c_web_server_sign2 = '%s';\n" % sign2 varblock += "document.s_s_c_debugmode=1;\n" fragment = tag(tag.script(varblock, type='text/javascript')) fragment.append( tag.script(type='text/javascript', src='http://backs.keycaptcha.com/swfs/cap.js') ) fragment.append( tag.input(type='hidden', id='keycaptcha_response_field', name='keycaptcha_response_field') ) fragment.append( tag.input(type='submit', id='keycaptcha_response_button', name='keycaptcha_response_button') ) req.session['captcha_key_session'] = session_id return None, fragment
def generate_captcha(self, req): add_script(req, 'https://www.google.com/recaptcha/api.js') return None, tag( tag.div(class_='g-recaptcha', data_sitekey=self.public_key), tag.input(type='submit', value=_("Submit")) )
def render_group(group): return tag.ul( tag.li( tag(tag.strong(elt[0].strip('/')), render_group(elt[1]) ) if isinstance(elt, tuple) else tag. a(wiki.format_page_name(omitprefix(elt)), href=formatter.href.wiki(elt))) for elt in group)
def render_ticket_action_control(self, req, ticket, action): id, grade = self._get_grade(req, action) review_options = self._get_review_options(action) actions = ConfigurableTicketWorkflow(self.env).actions selected_value = grade or review_options[0][0] label = actions[action]['label'] control = tag([ "as: ", tag.select([ tag.option(option, selected=(option == selected_value or None)) for option, status in review_options ], name=id, id=id) ]) if grade: new_status = self._get_new_status(req, ticket, action, review_options) hint = "Next status will be '%s'" % new_status else: hint = "Next status will be one of " + \ ', '.join("'%s'" % status for option, status in review_options) return label, control, hint
def _get_action_controls(self, req, tickets): action_controls = [] ts = TicketSystem(self.env) tickets_by_action = {} for t in tickets: ticket = Ticket(self.env, t['id']) available_actions = ts.get_available_actions(req, ticket) for action in available_actions: tickets_by_action.setdefault(action, []).append(ticket) # Sort the allowed actions by the 'default' key. allowed_actions = set(tickets_by_action) workflow = ConfigurableTicketWorkflow(self.env) all_actions = sorted(((action['default'], name) for name, action in workflow.get_all_actions().iteritems()), reverse=True) sorted_actions = [action[1] for action in all_actions if action[1] in allowed_actions] for action in sorted_actions: first_label = None hints = [] widgets = [] ticket = tickets_by_action[action][0] for controller in self._get_action_controllers(req, ticket, action): label, widget, hint = controller.render_ticket_action_control( req, ticket, action) if not first_label: first_label = label widgets.append(widget) hints.append(hint) action_controls.append((action, first_label, tag(widgets), hints)) return action_controls
def __get__(self, instance, owner): if instance is None: return self order = ListOption.__get__(self, instance, owner) components = [] implementing_classes = [] for impl in self.xtnpt.extensions(instance): implementing_classes.append(impl.__class__.__name__) if self.include_missing or impl.__class__.__name__ in order: components.append(impl) not_found = sorted(set(order) - set(implementing_classes)) if not_found: raise ConfigurationError( tag_("Cannot find implementation(s) of the %(interface)s " "interface named %(implementation)s. Please check " "that the Component is enabled or update the option " "%(option)s in trac.ini.", interface=tag.code(self.xtnpt.interface.__name__), implementation=tag( (', ' if idx != 0 else None, tag.code(impl)) for idx, impl in enumerate(not_found)), option=tag.code("[%s] %s" % (self.section, self.name)))) def key(impl): name = impl.__class__.__name__ if name in order: return 0, order.index(name) else: return 1, components.index(impl) return sorted(components, key=key)
def test_tracerror_with_tracerror_with_fragment(self): message = tag('Powered by ', tag.a('Trac', href='http://trac.edgewall.org/')) rv = to_fragment(TracError(TracError(message))) self.assertEqual(Fragment, type(rv)) self.assertEqual('Powered by <a href="http://trac.edgewall.org/">Trac' '</a>', unicode(rv))
def test_tag(self): self.assertEqual(Markup('0<a>0</a> and <b>0</b> and <c></c>' ' and <d class="a b" more_="[\'a\']"></d>'), Markup(tag(0, tag.a(0, href=''), b' and ', tag.b(0.0), ' and ', tag.c(None), ' and ', tag.d('', class_=['a', '', 'b'], more__=[b'a']))))
def post_process_request(self, req, template, data, content_type): if template is None or not req.session.authenticated: # Don't start the email verification procedure on anonymous users. return template, data, content_type email = req.session.get('email') # Only send verification if the user entered an email address. if self.verify_email and self.email_enabled is True and email and \ email != req.session.get('email_verification_sent_to') and \ 'ACCTMGR_ADMIN' not in req.perm: req.session['email_verification_token'] = self._gen_token() req.session['email_verification_sent_to'] = email try: AccountManager(self.env)._notify( 'email_verification_requested', req.authname, req.session['email_verification_token'] ) except NotificationError, e: chrome.add_warning(req, _( "Error raised while sending a change notification." ) + _("You should report that issue to a Trac admin.")) self.log.error('Unable to send registration notification: %s', exception_to_unicode(e, traceback=True)) else: # TRANSLATOR: An email has been sent to <%(email)s> # with a token to ... (the link label for following message) link = tag.a(_("verify your new email address"), href=req.href.verify_email()) # TRANSLATOR: ... verify your new email address chrome.add_notice(req, tag_( "An email has been sent to <%(email)s> with a token to " "%(link)s.", email=tag(email), link=link))
def linebreaks(value): """Converts newlines in strings into <p> and <br />s.""" if not value: return '' value = re.sub(r'\r\n|\r|\n', '\n', value) # normalize newlines paras = re.split('\n{2,}', value) return tag(tag.p((line, tag.br) for line in para.splitlines()) for para in paras)
def test_find_element_with_tag(self): frag = tag(tag.p('Paragraph with a ', tag.a('link', href='http://www.edgewall.org'), ' and some ', tag.strong('strong text'))) self.assertIsNotNone(find_element(frag, tag='p')) self.assertIsNotNone(find_element(frag, tag='a')) self.assertIsNotNone(find_element(frag, tag='strong')) self.assertIsNone(find_element(frag, tag='input')) self.assertIsNone(find_element(frag, tag='textarea'))
def create_graph_data(self, req, name=''): txt = req.args.get('text') if txt: actions, error_txt = get_workflow_actions_from_text(txt) if error_txt: t = error_txt else: t = "New custom workflow (not saved)" if not actions: # We should never end here... actions = get_workflow_config_by_type(self.config, 'default') t = "Custom workflow is broken. Showing default workflow" else: t = u"" print(name) if name == 'default': actions = get_workflow_config_by_type(self.config, 'default') else: actions = get_workflow_config_by_type(self.config, name) states = list(set( [state for action in actions.itervalues() for state in action['oldstates']] + [action['newstate'] for action in actions.itervalues()])) action_labels = [action_info['label'] for action_name, action_info in actions.items()] action_names = actions.keys() edges = [] for name, action in actions.items(): new_index = states.index(action['newstate']) name_index = action_names.index(name) for old_state in action['oldstates']: old_index = states.index(old_state) edges.append((old_index, new_index, name_index)) args = {} width = args.get('width', 800) height = args.get('height', 600) graph = {'nodes': states, 'actions': action_labels, 'edges': edges, 'width': width, 'height': height} graph_id = '%012x' % id(self) # id(graph) scr_data = {'graph_%s' % graph_id: graph} res = tag( tag.p(t), tag.div('', class_='multiple-workflow-graph trac-noscript', id='trac-workflow-graph-%s' % graph_id, style="display:inline-block;width:%spx;height:%spx" % (width, height)), tag.noscript( tag.div(_("Enable JavaScript to display the workflow graph."), class_='system-message'))) return res, scr_data, graph
def render_timeline_event(self, context, field, event): bp_resource, bp, bc = event[3] if bc: # A blog comment if field == 'url': return context.href.blog(bp.name) + '#comment-%d' % bc.number elif field == 'title': return tag('Blog: ', tag.em(bp.title), ' comment added') elif field == 'description': return format_to_oneliner( self.env, context(resource=bp_resource), bc.comment) else: # A blog post if field == 'url': return context.href.blog(bp.name) elif field == 'title': return tag('Blog: ', tag.em(bp.title), bp.version > 1 and ' edited' or ' created') elif field == 'description': return format_to_oneliner( self.env, context(resource=bp_resource), bp.version_comment)
def expand_macro(self, formatter, name, text, args): if not text: raw_actions = self.config.options('ticket-workflow') else: if args is None: text = '\n'.join(line.lstrip() for line in text.split(';')) if '[ticket-workflow]' not in text: text = '[ticket-workflow]\n' + text parser = RawConfigParser() try: parser.readfp(io.StringIO(text)) except ParsingError as e: return system_message(_("Error parsing workflow."), unicode(e)) raw_actions = list(parser.items('ticket-workflow')) actions = parse_workflow_config(raw_actions) states = list({ state for action in actions.itervalues() for state in action['oldstates'] } | {action['newstate'] for action in actions.itervalues()}) action_labels = [attrs['label'] for attrs in actions.values()] action_names = actions.keys() edges = [] for name, action in actions.items(): new_index = states.index(action['newstate']) name_index = action_names.index(name) for old_state in action['oldstates']: old_index = states.index(old_state) edges.append((old_index, new_index, name_index)) args = args or {} width = args.get('width', 800) height = args.get('height', 600) graph = { 'nodes': states, 'actions': action_labels, 'edges': edges, 'width': width, 'height': height } graph_id = '%012x' % id(graph) req = formatter.req add_script(req, 'common/js/excanvas.js', ie_if='IE') add_script(req, 'common/js/workflow_graph.js') add_script_data(req, {'graph_%s' % graph_id: graph}) return tag( tag.div('', class_='trac-workflow-graph trac-noscript', id='trac-workflow-graph-%s' % graph_id, style="display:inline-block;width:%spx;height:%spx" % (width, height)), tag.noscript( tag.div(_("Enable JavaScript to display the workflow graph."), class_='system-message')))
def _test_add_message_escapes_markup(self, msgtype, add_fn): req = MockRequest(self.env) add_fn(req, 'Message with an "&"') add_fn(req, Exception("Exception message with an &")) add_fn(req, tag("Message with text ", tag.b("& markup"))) add_fn(req, Markup("Markup <strong>message</strong>.")) messages = req.chrome[msgtype] self.assertIn('Message with an "&"', messages) self.assertIn("Exception message with an &", messages) self.assertIn("Message with text <b>& markup</b>", messages) self.assertIn("Markup <strong>message</strong>.", messages)
def _format_link(self, formatter, ns, target, label): link, params, fragment = formatter.split_link(target) ids = link.split(':', 2) attachment = None if len(ids) == 3: known_realms = ResourceSystem(self.env).get_known_realms() # new-style attachment: TracLinks (filename:realm:id) if ids[1] in known_realms: attachment = Resource(ids[1], ids[2]).child(self.realm, ids[0]) else: # try old-style attachment: TracLinks (realm:id:filename) if ids[0] in known_realms: attachment = Resource(ids[0], ids[1]).child(self.realm, ids[2]) else: # local attachment: TracLinks (filename) attachment = formatter.resource.child(self.realm, link) if attachment and 'ATTACHMENT_VIEW' in formatter.perm(attachment): try: Attachment(self.env, attachment) except ResourceNotFound: pass else: raw_href = get_resource_url(self.env, attachment, formatter.href, format='raw') title = get_resource_name(self.env, attachment) if ns.startswith('raw'): return tag.a(label, class_='attachment', href=raw_href + params, title=title) href = get_resource_url(self.env, attachment, formatter.href) return tag( tag.a(label, class_='attachment', title=title, href=href + params), tag.a(u'\u200b', class_='trac-rawlink', href=raw_href + params, title=_("Download"))) # FIXME: should be either: # # model = Attachment(self.env, attachment) # if model.exists: # ... # # or directly: # # if attachment.exists: # # (related to #4130) return tag.a(label, class_='missing attachment')
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content) format = kwargs.get('format', 'compact') glob = kwargs.get('glob', '*') order = kwargs.get('order') desc = as_bool(kwargs.get('desc', 0)) rm = RepositoryManager(self.env) all_repos = dict(rdata for rdata in rm.get_all_repositories().items() if fnmatchcase(rdata[0], glob)) if format == 'table': repo = self._render_repository_index(formatter.context, all_repos, order, desc) add_stylesheet(formatter.req, 'common/css/browser.css') wiki_format_messages = self.config['changeset'] \ .getbool('wiki_format_messages') data = {'repo': repo, 'order': order, 'desc': 1 if desc else None, 'reponame': None, 'path': '/', 'stickyrev': None, 'wiki_format_messages': wiki_format_messages} return Chrome(self.env).render_fragment(formatter.context.req, 'repository_index.html', data) def get_repository(reponame): try: return rm.get_repository(reponame) except TracError: return all_repos = [(reponame, get_repository(reponame)) for reponame in all_repos] all_repos = sorted(((reponame, repos) for reponame, repos in all_repos if repos and not as_bool(repos.params.get('hidden')) and repos.is_viewable(formatter.perm)), reverse=desc) def repolink(reponame, repos): label = reponame or _('(default)') return Markup(tag.a(label, title=_('View repository %(repo)s', repo=label), href=formatter.href.browser(repos.reponame or None))) if format == 'list': return tag.dl([ tag(tag.dt(repolink(reponame, repos)), tag.dd(repos.params.get('description'))) for reponame, repos in all_repos]) else: # compact return Markup(', ').join(repolink(reponame, repos) for reponame, repos in all_repos)
def post_process_request(self, req, template, data, content_type, method=None): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type, method tkt = data['ticket'] with self.env.db_query as db: links = CrashDumpTicketLinks(self.env, tkt, db=db) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.crashdump_link_fields: if field_data['new'].strip(): new = set([ CrashDumpSystem.get_crash_id(n) for n in field_data['new'].split(',') ]) else: new = set() if field_data['old'].strip(): old = set([ CrashDumpSystem.get_crash_id(n) for n in field_data['old'].split(',') ]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms links.crashes = new return template, data, content_type, method
def render_action_control(self, req, ticket, action): first_label = None widgets = [] hints = [] for controller in self.controllers_for_action(req, ticket, action): label, widget, hint = controller.render_ticket_action_control( req, ticket, action) if first_label is None: first_label = label widgets.append(widget) hints.append(unicode(hint)) hints = hints and '. '.join(hints) or '' return first_label, tag(*widgets), hints
def get_existing_node(req, repos, path, rev): try: return repos.get_node(path, rev) except NoSuchNode as e: # TRANSLATOR: You can 'search' in the repository history... (link) search_a = tag.a(_("search"), href=req.href.log(repos.reponame or None, path, rev=rev, mode='path_history')) raise ResourceNotFound(tag( tag.p(e, class_="message"), tag.p(tag_("You can %(search)s in the repository history to see " "if that path existed but was later removed", search=search_a))))
def _wiki_edit(self, req, stream): tags = ' '.join(self._page_tags(req)) # TRANSLATOR: Label text for link to '/tags'. link = tag.a(_("view all tags"), href=req.href.tags()) # TRANSLATOR: ... (view all tags) insert = tag( tag_("Tag under: (%(tags_link)s)", tags_link=link), tag.br(), tag.input(id='tags', type='text', name='tags', size='50', value=req.args.get('tags', tags))) insert = tag.div(tag.label(insert), class_='field') return stream | Transformer('//div[@id="changeinfo1"]').append(insert)
def process_request(self, req): if req.path_info.startswith('/login') and req.authname == 'anonymous': try: referer = self._referer(req) except AttributeError: # Fallback for Trac 0.11 compatibility. referer = req.get_header('Referer') # Steer clear of requests going nowhere or loop to self. if referer is None or \ referer.startswith(str(req.abs_href()) + '/login'): referer = req.abs_href() data = { '_dgettext': dgettext, 'login_opt_list': self.login_opt_list, 'persistent_sessions': AccountManager(self.env).persistent_sessions, 'referer': referer, 'registration_enabled': RegistrationModule(self.env).enabled, 'reset_password_enabled': AccountModule(self.env).reset_password_enabled } if req.method == 'POST': self.log.debug("LoginModule.process_request: 'user_locked' " "= %s", req.args.get('user_locked')) if not req.args.get('user_locked'): # TRANSLATOR: Intentionally obfuscated login error data['login_error'] = _("Invalid username or password") else: f_user = req.args.get('username') release_time = AccountGuard(self.env ).pretty_release_time(req, f_user) if release_time is not None: data['login_error'] = \ _("Account locked, please try again after % " "(release_time)s", release_time=release_time) else: data['login_error'] = _("Account locked") return 'login.html', data, None else: n_plural = req.args.get('failed_logins') if n_plural > 0: add_warning(req, tag(ngettext( "Login after %(attempts)s failed attempt", "Login after %(attempts)s failed attempts", n_plural, attempts=n_plural ))) return auth.LoginModule.process_request(self, req)
def _render_link(self, context, name, label, extra=''): if not (name or extra): return tag() try: milestone = Milestone(self.env, name) except ResourceNotFound: milestone = None # Note: the above should really not be needed, `Milestone.exists` # should simply be false if the milestone doesn't exist in the db # (related to #4130) href = context.href.milestone(name) exists = milestone and milestone.exists if exists: if 'MILESTONE_VIEW' in context.perm(milestone.resource): title = None if hasattr(context, 'req'): if milestone.is_completed: title = _("Completed %(duration)s ago (%(date)s)", duration=pretty_timedelta( milestone.completed), date=user_time(context.req, format_datetime, milestone.completed)) elif milestone.is_late: title = _("%(duration)s late (%(date)s)", duration=pretty_timedelta(milestone.due), date=user_time(context.req, format_datetime, milestone.due)) elif milestone.due: title = _("Due in %(duration)s (%(date)s)", duration=pretty_timedelta(milestone.due), date=user_time(context.req, format_datetime, milestone.due)) else: title = _("No date set") closed = 'closed ' if milestone.is_completed else '' return tag.a(label, class_='%smilestone' % closed, href=href + extra, title=title) elif 'MILESTONE_CREATE' in context.perm(self.realm, name): return tag.a(label, class_='missing milestone', href=href + extra, rel='nofollow') return tag.a(label, class_=classes('milestone', missing=not exists))
def _post_process_request_wiki_edit(self, req): tags = ' '.join(self._page_tags(req)) # TRANSLATOR: Label text for link to '/tags'. link = tag.a(_("view all tags"), href=req.href.tags()) # TRANSLATOR: ... (view all tags) insert = tag( tag_("Tag under: (%(tags_link)s)", tags_link=link), tag.br(), tag.input(id='tags', type='text', name='tags', size='50', value=req.args.get('tags', tags))) insert = tag.div(tag.label(insert), class_='field') filter_lst = [] # xpath = //div[@id="changeinfo1"] xform = JTransformer('div#changeinfo1') filter_lst.append(xform.append(Markup(insert))) self._add_jtransform(req, filter_lst)
def _format_browser_link(self, formatter, ns, path, label): path, query, fragment = formatter.split_link(path) rev = marks = None match = self.PATH_LINK_RE.match(path) if match: path, rev, marks = match.groups() href = formatter.href src_href = href.browser(path, rev=rev, marks=marks) + query + fragment node, raw_href, title = self._get_link_info(path, rev, formatter.href, formatter.perm) if not node: return tag.a(label, class_='missing source') link = tag.a(label, class_='source', href=src_href) if raw_href: link = tag(link, tag.a(u'\u200b', href=raw_href + fragment, title=title, class_='trac-rawlink' if node.isfile else 'trac-ziplink')) return link
def render_timeline_event(self, context, field, event): wiki_page, comment = event[3] if field == 'url': return context.href.wiki(wiki_page.id, version=wiki_page.version) elif field == 'title': name = tag.em(get_resource_name(self.env, wiki_page)) if wiki_page.version > 1: return tag_("%(page)s edited", page=name) else: return tag_("%(page)s created", page=name) elif field == 'description': markup = format_to(self.env, None, context.child(resource=wiki_page), comment) if wiki_page.version > 1: diff_href = context.href.wiki( wiki_page.id, version=wiki_page.version, action='diff') markup = tag(markup, " (", tag.a(_("diff"), href=diff_href), ")") return markup
def render_ticket_action_control(self, req, ticket, action): id, selected = self._get_selected(req, action) review_options = self._get_review_options(action) actions = ConfigurableTicketWorkflow(self.env).actions label = actions[action]['label'] control = tag(["as: ", tag.select([ tag.option(option, selected=(option == selected or None)) for option in review_options], name=id, id=id)]) if selected: new_status = self._get_new_status(req, action, review_options) hint = "Next status will be '%s'" % new_status else: hint = "Next status will be one of " + \ ', '.join("'%s'" % st for st in review_options.itervalues()) return label, control, hint
def _link_tickets(self, req, tickets): if tickets is None: return None if not isinstance(tickets, str) and not isinstance(tickets, unicode): self.log.debug('_link_tickets %s invalid type (%s)' % (tickets, type(tickets))) return None if not tickets: return None items = [] for i, word in enumerate(re.split(r'([;,\s]+)', tickets)): if i % 2: items.append(word) elif word: ticketid = word word = '#%s' % word try: ticket = Ticket(self.env, ticketid) if 'TICKET_VIEW' in req.perm(ticket.resource): word = \ tag.a( '#%s' % ticket.id, class_=ticket['status'], href=req.href.ticket(int(ticket.id)), title=shorten_line(ticket['summary']) ) except ResourceNotFound: pass items.append(word) if items: return tag(items) else: return None
def _get_action_controls(self, req, ticket_data): tickets = [Ticket(self.env, t['id']) for t in ticket_data] action_weights = {} action_tickets = {} for t in tickets: for ctrl in TicketSystem(self.env).action_controllers: for weight, action in ctrl.get_ticket_actions(req, t) or []: if action in action_weights: action_weights[action] = max(action_weights[action], weight) action_tickets[action].append(t) else: action_weights[action] = weight action_tickets[action] = [t] sorted_actions = [ a for a, w in sorted(action_weights.iteritems(), key=lambda item: (item[1], item[0]), reverse=True) ] action_controls = [] for action in sorted_actions: first_label = None hints = [] widgets = [] ticket = action_tickets[action][0] for controller in self._get_action_controllers( req, ticket, action): label, widget, hint = controller.render_ticket_action_control( req, ticket, action) if not first_label: first_label = label widgets.append(widget) hints.append(hint) action_controls.append((action, first_label, tag(widgets), hints)) return action_controls
def _provider_failure(self, exc, req, ep, current_filters, all_filters): """Raise a TracError exception explaining the failure of a provider. At the same time, the message will contain a link to the timeline without the filters corresponding to the guilty event provider `ep`. """ self.log.error("Timeline event provider failed: %s", exception_to_unicode(exc, traceback=True)) ep_kinds = {f[0]: f[1] for f in ep.get_timeline_filters(req) or []} ep_filters = set(ep_kinds.keys()) current_filters = set(current_filters) other_filters = set(current_filters) - ep_filters if not other_filters: other_filters = set(all_filters) - ep_filters args = [(a, req.args.get(a)) for a in ('from', 'format', 'max', 'daysback')] href = req.href.timeline(args + [(f, 'on') for f in other_filters]) # TRANSLATOR: ...want to see the 'other kinds of events' from... (link) other_events = tag.a(_('other kinds of events'), href=href) raise TracError( tag( tag.p(tag_( "Event provider %(name)s failed for filters " "%(kinds)s: ", name=tag.code(ep.__class__.__name__), kinds=', '.join('"%s"' % ep_kinds[f] for f in current_filters & ep_filters)), tag.strong(exception_to_unicode(exc)), class_='message'), tag.p( tag_( "You may want to see the %(other_events)s from the " "Timeline or notify your Trac administrator about the " "error (detailed information was written to the log).", other_events=other_events))))
def filter_stream(self, req, method, filename, stream, data): if req.path_info.startswith('/register') and ( req.method == 'GET' or 'registration_error' in data or 'captcha_error' in req.session): if not (self.private_key or self.private_key): return stream captcha_opts = tag.script("""\ var RecaptchaOptions = { theme: "%s", lang: "%s" }""" % (self.theme, self.lang), type='text/javascript') captcha_js = captcha.displayhtml( self.public_key, use_ssl=req.scheme == 'https', error='reCAPTCHA incorrect. Please try again.', version=2 ) + captcha.load_script(version=2) # First Fieldset of the registration form XPath match xpath_match = '//form[@id="acctmgr_registerform"]/fieldset[1]' return stream | Transformer(xpath_match). \ append(captcha_opts + tag(Markup(captcha_js))) # Admin Configuration elif req.path_info.startswith('/admin/accounts/config'): api_html = tag.div( tag.label("Public Key:", for_="recaptcha_public_key") + tag.input(class_="textwidget", name="recaptcha_public_key", value=self.public_key, size=40) ) + tag.div( tag.label("Private Key:", for_="recaptcha_private_key") + tag.input(class_="textwidget", name="recaptcha_private_key", value=self.private_key, size=40) ) if not (self.private_key or self.public_key): api_html = tag.div( tag.a("Generate a reCAPTCHA API key for this Trac " "instance domain.", target="_blank", href="http://recaptcha.net/api/getkey?domain=%s&" "app=TracRecaptchaRegister" % req.environ.get('SERVER_NAME') ) ) + tag.br() + api_html theme_html = tag.div( tag.label("reCPATCHA theme:", for_='recaptcha_theme') + tag.select( tag.option("Black Glass", value="blackglass", selected=self.theme == 'blackglass' or None) + tag.option("Clean", value="clean", selected=self.theme == 'clean' or None) + tag.option("Red", value="red", selected=self.theme == 'red' or None) + tag.option("White", value="white", selected=self.theme == 'white' or None), name='recaptcha_theme' ) ) language_html = tag.div( tag.label("reCAPTCHA language:", for_='recaptcha_lang') + tag.select( tag.option("Dutch", value="nl", selected=self.lang == 'nl' or None) + tag.option("English", value="en", selected=self.lang == 'en' or None) + tag.option("French", selected=self.lang == 'fr' or None) + tag.option("German", value="de", selected=self.lang == 'de' or None) + tag.option("Portuguese", value="pt", selected=self.lang == 'pt' or None) + tag.option("Russian", value="ru", selected=self.lang == 'ru' or None) + tag.option("Spanish", value="es", selected=self.lang == 'es' or None) + tag.option("Turkish", value="tr", selected=self.lang == 'tr' or None), name='recaptcha_lang')) # First fieldset of the Account Manager config form xpath_match = '//form[@id="cfg_wiz"]/fieldset[1]' return stream | Transformer(xpath_match). \ before(tag.fieldset(tag.legend("reCAPTCHA") + api_html + tag.br() + theme_html + language_html)) return stream
def render_groups(groups): return tag.ul( [tag.li(isinstance(elt, tuple) and tag(tag.strong(elt[0]), render_groups(elt[1])) or render_one(elt, pagelangs.get(elt))) for elt in groups])
def expand_macro(self, formatter, name, content): env = formatter.env req = formatter.req if not content: args = [] kw = {} else: args, kw = parse_args(content) if name == 'ProjectStats': if 'wiki' in kw.keys(): prefix = 'prefix' in kw.keys() and kw['prefix'] or None wiki = WikiSystem(env) if kw['wiki'] == 'count' or 'count' in args: return tag(len(list(wiki.get_pages(prefix)))) elif name == 'UserQuery': msg_no_perm = tag.p(tag_("(required %(perm)s missing)", perm=tag.strong('USER_VIEW')), class_='hint') if 'perm' in kw.keys(): perm_sys = PermissionSystem(self.env) users = perm_sys.get_users_with_permission(kw['perm'].upper()) else: acct_mgr = AccountManager(env) users = list(set(acct_mgr.get_users())) if 'locked' in kw.keys() or 'locked' in args: guard = AccountGuard(env) locked = [] for user in users: if guard.user_locked(user): locked.append(user) if kw.get('locked', 'True').lower() in ('true', 'yes', '1'): users = locked else: users = list(set(users) - set(locked)) elif 'visit' in kw.keys() or 'visit' in args: if 'USER_VIEW' not in req.perm: return msg_no_perm cols = [] data = {'accounts': fetch_user_data(env, req), 'cls': 'wiki'} for col in ('email', 'name'): if col in args: cols.append(col) data['cols'] = cols return Chrome(env).render_template( req, 'user_table.html', data, 'text/html', True) if kw.get('format') == 'count' or 'count' in args: return tag(len(users)) if 'USER_VIEW' not in req.perm: return msg_no_perm if 'email' in args or 'name' in args: # Replace username with full name, add email if available. for username, name, email in self.env.get_known_users(): if username in users: if 'name' not in args or name is None: name = username if 'email' in args and email is not None: email = ''.join(['<', email, '>']) name = ' '.join([name, email]) if not username == name: users.pop(users.index(username)) users.append(name) if not users and 'nomatch' in kw.keys(): return format_to_oneliner(env, formatter.context, kw['nomatch']) users = sorted(users) if kw.get('format') == 'list': return tag.ul([tag.li(Chrome(env).format_author(req, user)) for user in users]) else: # Default output format: comma-separated list. return tag(', '.join([Chrome(env).format_author(req, user) for user in users]))