def _do_rename(self, req, page): if page.readonly: req.perm(page.resource).require('WIKI_ADMIN') else: req.perm(page.resource).require('WIKI_RENAME') if 'cancel' in req.args: req.redirect(get_resource_url(self.env, page.resource, req.href)) old_name, old_version = page.name, page.version new_name = req.args.get('new_name', '').rstrip('/') redirect = req.args.get('redirect') # verify input parameters warn = None if not new_name: warn = _('A new name is mandatory for a rename.') elif new_name == old_name: warn = _('The new name must be different from the old name.') elif WikiPage(self.env, new_name).exists: warn = _('The page %(name)s already exists.', name=new_name) if warn: add_warning(req, warn) return self._render_confirm_rename(req, page, new_name) @self.env.with_transaction() def do_rename(db): page.rename(new_name) if redirect: redirection = WikiPage(self.env, old_name, db=db) redirection.text = _('See [wiki:"%(name)s"].', name=new_name) author = get_reporter_id(req) comment = u'[wiki:"%s@%d" %s] \u2192 [wiki:"%s"].' % ( new_name, old_version, old_name, new_name) redirection.save(author, comment, req.remote_addr) req.redirect(req.href.wiki(redirect and old_name or new_name))
def _do_delete(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') retarget_to = req.args.get('target') or None # Don't translate ticket comment (comment:40:ticket:5658) retargeted_tickets = \ milestone.move_tickets(retarget_to, req.authname, "Ticket retargeted after milestone deleted") milestone.delete(author=req.authname) add_notice(req, _('The milestone "%(name)s" has been deleted.', name=milestone.name)) if retargeted_tickets: add_notice(req, _('The tickets associated with milestone ' '"%(name)s" have been retargeted to milestone ' '"%(retarget)s".', name=milestone.name, retarget=retarget_to)) new_values = {'milestone': retarget_to} comment = _("Tickets retargeted after milestone deleted") tn = BatchTicketNotifyEmail(self.env) try: tn.notify(retargeted_tickets, new_values, comment, None, req.authname) except Exception, e: self.log.error("Failure sending notification on ticket batch " "change: %s", exception_to_unicode(e)) add_warning(req, tag_("The changes have been saved, but an " "error occurred while sending " "notifications: %(message)s", message=to_unicode(e)))
def process_request(self, req): if not req.session.authenticated: chrome.add_warning(req, tag_( "Please log in to finish email verification procedure.")) req.redirect(req.href.login()) if 'email_verification_token' not in req.session: chrome.add_notice(req, _("Your email is already verified.")) elif req.method == 'POST' and 'resend' in req.args: 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 verification notification: %s', exception_to_unicode(e, traceback=True)) else: chrome.add_notice(req, _("A notification email has been " "resent to <%s>."), req.session.get('email'))
def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TRAC_ADMIN') status, message = init_admin(self.gitosis_user, self.gitosis_server, self.admrepo, self.env.path) data = {} if status != 0: add_warning(req, _('Error while cloning gitosis-admin repository. Please check your settings and/or passphrase free connection to this repository for the user running trac (in most cases, the web server user)')) message = 'return code: '+str(status)+'\nmessage:\n'+message if message: add_warning(req, _(message)) repo = replace(os.path.basename(self.config.get('trac', 'repository_dir')), '.git', '') if req.method == 'POST': config = {} self.log.debug('description: '+req.args.get('description')) for option in ('daemon', 'gitweb', 'description', 'owner'): config[option] = req.args.get(option) self.set_config(repo, config) req.redirect(req.href.admin(cat, page)) repo = replace(os.path.basename(self.config.get('trac', 'repository_dir')), '.git', '') if repo != '': data = self.get_config(repo) self.log.debug('data: %s', str(data)) if not data: data = {} for option in ('daemon', 'gitweb', 'description', 'owner'): if option not in data: data[option] = '' data['gitweb'] = data['gitweb'] in _TRUE_VALUES data['daemon'] = data['daemon'] in _TRUE_VALUES return 'admin_tracgitosis_repo.html', {'repo': data}
def _process_changes(self, req, protos, scm_type): scm_protos = dav_protos = set([]) allowed_scm_schemes = protos.allowed_protocols(scm_type) allowed_dav_schemes = protos.allowed_protocols('dav') if 'scm_proto' in req.args: scm_protos = set(self._to_list(req.args['scm_proto'])) if 'dav_proto' in req.args: dav_protos = set(self._to_list(req.args['dav_proto'])) if not self._validate(scm_protos, dav_protos): msg = 'Changes not stored, make sure that at least one protocol for each ' \ 'section is selected. If you want to disable any section, configure ' \ 'the appropriate permissions in the "Groups" page' add_warning(req, msg) return try: # Change scm protocols protos.disallow_protocols(allowed_scm_schemes - scm_protos, scm_type) protos.allow_protocols(scm_protos - allowed_scm_schemes, scm_type) # Change dav protocols protos.disallow_protocols(allowed_dav_schemes - dav_protos, 'dav') protos.allow_protocols(dav_protos - allowed_dav_schemes, 'dav') except: raise TracError("Server error. Try again later.") add_notice(req, "Changes saved")
def render_admin_panel(self, req, cat, page, version): self.log.debug("cat: %s page: %s", cat, page) req.perm.require('TRAC_ADMIN') options = ( 'api_base_url', 'api_token', 'room_id', 'only_owner_changed', 'notify_symbol', 'api_token_field_name') self.log.debug("method: %s", req.method) if req.method == 'POST': for option in options: self.config.set(SECTION_NAME, option, req.args.get(option)) try: self.config.save() self.log.debug('config saved.') add_notice(req, 'Your changes have been saved.') except Exception, e: self.log.error("Error writing to trac.ini: %s", exception_to_unicode(e)) add_warning(req, 'Error writing to trac.ini.') req.redirect(req.href.admin(cat, page))
def _do_account(self, req): assert(req.authname and req.authname != 'anonymous') action = req.args.get('action') delete_enabled = self.acctmgr.supports('delete_user') and \ self.acctmgr.allow_delete_account data = {'delete_enabled': delete_enabled, 'delete_msg_confirm': _( "Are you sure you want to delete your account?"), } force_change_password = req.session.get('force_change_passwd', False) if req.method == 'POST': if action == 'save': data.update(self._do_change_password(req)) if force_change_password: del(req.session['force_change_passwd']) req.session.save() chrome.add_notice(req, Markup(tag.span(tag_( "Thank you for taking the time to update your password." )))) force_change_password = False elif action == 'delete' and delete_enabled: data.update(self._do_delete(req)) else: data.update({'error': 'Invalid action'}) if force_change_password: chrome.add_warning(req, Markup(tag.span(_( "You are required to change password because of a recent " "password change request. "), tag.b(_("Please change your password now."))))) return data
def pre_process_request(self, req, handler): if not req.authname or req.authname == 'anonymous': # Permissions for anonymous users remain unchanged. return handler elif req.path_info == '/prefs' and \ req.method == 'POST' and \ 'restore' not in req.args and \ req.get_header( 'X-Requested-With') != 'XMLHttpRequest': try: AccountManager(self.env).validate_account(req) # Check passed without error: New email address seems good. except RegistrationError, e: # Always warn about issues. chrome.add_warning(req, e) # Look, if the issue existed before. attributes = get_user_attribute(self.env, req.authname, attribute='email') email = req.authname in attributes and \ attributes[req.authname][1].get('email') or None new_email = req.args.get('email', '').strip() if (email or new_email) and email != new_email: # Attempt to change email to an empty or invalid # address detected, resetting to previously stored value. req.redirect(req.href.prefs(None))
def _render_source(self, context, stream, annotations, marks=None): from trac.web.chrome import add_warning annotators, labels, titles = {}, {}, {} for annotator in self.annotators: atype, alabel, atitle = annotator.get_annotation_type() if atype in annotations: labels[atype] = alabel titles[atype] = atitle annotators[atype] = annotator annotations = [a for a in annotations if a in annotators] if isinstance(stream, list): stream = HTMLParser(StringIO(u'\n'.join(stream))) elif isinstance(stream, unicode): text = stream def linesplitter(): for line in text.splitlines(True): yield TEXT, line, (None, -1, -1) stream = linesplitter() annotator_datas = [] for a in annotations: annotator = annotators[a] try: data = (annotator, annotator.get_annotation_data(context)) except TracError, e: self.log.warning("Can't use annotator '%s': %s", a, e.message) add_warning(context.req, tag.strong( tag_("Can't use %(annotator)s annotator: %(error)s", annotator=tag.em(a), error=tag.pre(e.message)))) data = (None, None) annotator_datas.append(data)
def commit(self, req): """Perform the operation for all processed rows. Return a list of new or changed tickets""" tickets = [] if not self.do_preview: for fields in self.rows: ticket = self._get_ticket_from_id_in_csv(req, fields) if ticket == None: continue ticket_type = self._get_type_for_ticket(fields) if not self._may_create_ticket(req.perm, ticket_type): add_warning(req, _("No permission to create a %s.") % ticket_type) continue if not self.force: csv_summary = fields[Key.SUMMARY] db_summary = ticket[Key.SUMMARY] if csv_summary != db_summary: msg = _("Ticket %d has a different summary: '%s' (CSV) - '%s' (DB)") add_warning(req, msg % (ticket.id, repr(csv_summary), repr(db_summary))) continue tickets.append(ticket) ticket.delete() return tickets
def render_preference_panel(self, req, panel): """ Renders preference panel and handles image change on POST """ if req.authname == 'anonymous': raise TracError("User is not authenticated", "No access") userstore = get_userstore() user = userstore.getUser(req.authname) if req.method == 'POST': if 'removeicon' in req.args: user.icon = None userstore.updateUser(user) elif 'icon' in req.args: user.createIcon(req.args['icon']) if user.icon: userstore.updateUser(user) data = {'user':user, 'base_path':req.base_path} if 'limitexceeded' in req.args: add_warning(req, 'Picture you tried to upload was too big. Try a smaller one.') return 'multiproject_user_prefs_image.html', data
def render_preference_panel(self, req, panel): """ Renders preference panel and handles information change on POST """ if req.authname == 'anonymous': raise TracError("User is not authenticated", "No access") data = {} key_store = CQDESshKeyStore.instance() user = get_userstore().getUser(req.authname) if req.method == 'POST': ssh_key = req.args.get('ssh_key') delete_key = req.args.get('deletelist') if ssh_key: user = self._do_save(req, user) elif delete_key: user = self._do_deletes(req, user) else: add_warning(req, _('Please provide data')) keys = key_store.get_ssh_keys_by_user_id(user.id) data['keys'] = keys if keys else None # This is to prevent adding of more than one ssh password. # Remove this if we support more in the future. if keys: data['hide_add_dialog'] = False data['domain'] = conf.domain_name data['user'] = user return 'multiproject_user_prefs_ssh_keys.html', data
def setkeys(self, req, keys): if req.authname == 'anonymous': raise TracError('cannot set ssh keys for anonymous users') keys = set(keys) if len(keys) > 0x100: add_warning(req, 'We only support using your first 256 ssh keys.') return self._setkeys(req.authname, keys)
def get_ticket_changes(self, req, ticket, action): # WARNING: Directly modifying the ticket in this method breaks the # intent of this method. But it does accomplish the desired goal. if not 'preview' in req.args: id = 'action_%s_xref' % action ticketnum = req.args.get(id).strip('#') try: xticket = model.Ticket(self.env, ticketnum) except ValueError: req.args['preview'] = True add_warning(req, 'The cross-referenced ticket number "%s" was not a number' % ticketnum) return {} except ResourceNotFound, e: #put in preview mode to prevent ticket being saved req.args['preview'] = True add_warning(req, "Unable to cross-reference Ticket #%s (%s)." % (ticketnum, e.message)) return {} oldcomment = req.args.get('comment') actions = self.get_configurable_workflow().actions format_string = actions[action].get('xref_local', 'Ticket %s was marked as related to this ticket') # Add a comment to this ticket to indicate that the "remote" ticket is # related to it. (But only if <action>.xref_local was set in the # config.) if format_string: comment = format_string % ('#%s' % ticketnum) req.args['comment'] = "%s%s%s" % \ (comment, oldcomment and "[[BR]]" or "", oldcomment or "")
def post_new_artifact(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env, operation="CREATE") assert(obj is Instance or isinstance(obj, Entity)) # otherwise, we're trying to instantiate something that is not an artifact spec_name = request.req.args['spec'] if spec_name: try: dbp.load_spec(spec_name) spec = dbp.pool.get_item(spec_name) except ValueError: add_warning(request.req, "Spec '%s' not found, assumed an empty spec instead." % spec_name) spec = Instance else: spec = Instance values, str_attr = _group_artifact_values(request.req) brand_new_inst = spec(str_attr=str_attr, values=values) dbp.pool.add(brand_new_inst) dbp.save(get_reporter_id(request.req), 'comment', request.req.remote_addr) if request.get_format() == 'page': add_notice(request.req, 'Your changes have been saved.') url = request.req.href.customartifacts('artifact/%d' % (brand_new_inst.get_id(),), action='view', format=request.get_format()) request.req.redirect(url) else: import json url = request.req.href.customartifacts('artifact/%d' % (brand_new_inst.get_id(),), action='view') msg = json.dumps([{'result': 'success', 'resource_id': brand_new_inst.get_id(), 'resource_url': url}]) request.req.send_response(200) request.req.send_header('Content-Type', 'application/json') request.req.send_header('Content-Length', len(msg)) request.req.end_headers() request.req.write(msg)
def _do_change_password(self, req): username = req.authname old_password = req.args.get('old_password') if not self.acctmgr.check_password(username, old_password): if old_password: add_warning(req, _("Old password is incorrect.")) else: add_warning(req, _("Old password cannot be empty.")) return password = req.args.get('password') if not password: add_warning(req, _("Password cannot be empty.")) elif password != req.args.get('password_confirm'): add_warning(req, _("The passwords must match.")) elif password == old_password: add_warning(req, _("Password must not match old password.")) else: _set_password(self.env, req, username, password, old_password) if req.session.get('password') is not None: # Fetch all session_attributes in case new user password is in # SessionStore, preventing overwrite by session.save(). req.session.get_session(req.authname, authenticated=True) add_notice(req, _("Password updated successfully.")) return True
def process_request(self, req): if req.path_info == '/traccron/runtask': self._runtask(req) else: self.env.log.warn("Trac Cron Plugin was unable to handle %s" % req.path_info) add_warning(req, "The request was not handled by trac cron plugin") req.redirect(req.href.admin('traccron','cron_admin'))
def process_request(self, req): if not req.session.authenticated: chrome.add_warning(req, Markup(tag.span(tag_( "Please log in to finish email verification procedure."))) ) req.redirect(req.href.login()) if 'email_verification_token' not in req.session: chrome.add_notice(req, _("Your email is already verified.")) elif req.method == 'POST' and 'resend' in req.args: AccountManager(self.env)._notify( 'email_verification_requested', req.authname, req.session['email_verification_token'] ) chrome.add_notice(req, _("A notification email has been resent to <%s>."), req.session.get('email') ) elif 'verify' in req.args: # allow via POST or GET (the latter for email links) if req.args['token'] == req.session['email_verification_token']: del req.session['email_verification_token'] chrome.add_notice( req, _("Thank you for verifying your email address.") ) req.redirect(req.href.prefs()) else: chrome.add_warning(req, _("Invalid verification token")) data = {'_dgettext': dgettext} if 'token' in req.args: data['token'] = req.args['token'] if 'email_verification_token' not in req.session: data['button_state'] = { 'disabled': 'disabled' } return 'verify_email.html', data, None
def _reject_oauth(self, req, exc, reason=None): self.log.warn("An OAuth authorization attempt was rejected due to an " "exception: %s\n%s", exc, traceback.format_exc()) if reason is None: reason = _("Invalid request. Please try to login again.") add_warning(req, reason) self._redirect_back(req)
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 _add_organization(self, req, group_store): req.perm.require("PERMISSION_GRANT") group_name = req.args.get("group") organization = req.args.get("organization") try: group_store.add_organization_to_group(organization, group_name) add_notice( req, _( "Organization %(organization)s added to group %(group)s", group=group_name, organization=organization, ), ) except ValueError: add_warning( req, _( "Organization %(organization)s already exists in group %(group)s", group=group_name, organization=organization, ), )
def pre_process_request(self, req, handler): if isinstance(handler, RegistrationModule): if not (self.private_key or self.private_key): self.log.warning('public_key and private_key under [recaptcha] are ' 'not configured. Not showing the reCAPTCHA form!') return handler self.check_config() if req.method == 'POST' and req.args.get('action') == 'create': response = captcha.submit( req.args.get('recaptcha_challenge_field'), req.args.get('recaptcha_response_field'), self.private_key, req.remote_addr, ) if not response.is_valid: add_warning(req, 'reCAPTCHA incorrect. Please try again.') req.environ['REQUEST_METHOD'] = 'GET' req.args.pop('password', None) req.args.pop('password_confirm', None) # Admin Configuration if req.path_info.startswith('/admin/accounts/config') and \ req.method == 'POST': self.config.set('recaptcha', 'lang', req.args.get('recaptcha_lang')) self.config.set('recaptcha', 'public_key', req.args.get('recaptcha_public_key')) self.config.set('recaptcha', 'private_key', req.args.get('recaptcha_private_key')) self.config.set('recaptcha', 'theme', req.args.get('recaptcha_theme')) self.config.save() return handler
def pre_process_request(self, req, handler): """Called after initial handler selection, and can be used to change the selected handler or redirect request. Always returns the request handler, even if unchanged. """ if req.path_info.strip('/') == "register": if req.method == "POST": correct_answer = req.session.pop('captcha', None) req.session.save() if req.args['captcha'].lower() != correct_answer: # XXX this bizaarely doesn't work # req.session['captchaauth_message'] = "You typed the wrong word. Please try again." # req.session.save() req.redirect(req.href('register', failed='')) if req.method == "GET": if 'failed' in req.args: add_warning(req, "You typed the wrong word. Please try again.") # XXX this bizaarely doesn't work # message = req.session.pop('captchaauth_message', None) # if message: # add_warning(req, message) return handler
def _add_ldap_group(self, req, group_store): req.perm.require("PERMISSION_GRANT") group_name = req.args.get("group") ldap_group_name = req.args.get("ldap_group", "").strip() ldap_group_name = ldap_group_name.upper() if re.search(r"[^\_A-Z0-9]", ldap_group_name): # allowed characters add_warning(req, "LDAP group name can contain only alphanumeric characters and underline.") return if not ldap_group_name: add_warning( req, _( "You are trying to add an LDAP group to a user group, " "but you have not specified all the required parameters." ), ) return group_store.add_ldapgroup_to_group(ldap_group_name, group_name) add_notice( req, _("LDAP group %(who)s has been added to group %(where)s.", who=ldap_group_name, where=group_name) )
def save(self, req): if req.args and req.args.has_key('action') \ and req.args['action'] == 'save': for key in SESSION_KEYS.values(): if req.args.has_key(key): if key == 'wiki.href': wiki_href = req.args[key] if wiki_href == '': req.session[key] = '' continue validated = WikiSystem(self.env).has_page(wiki_href) if validated: req.session[key] = req.args[key] else: add_warning(req, Markup(tag.span(Markup(_( "%(page)s is not a valid Wiki page", page=tag.b(wiki_href) ))))) elif key == 'tickets.href': ticket_href = req.args[key] if ticket_href == '': req.session[key] = '' continue reports = self.get_report_list() self.log.info('reports: %s' % reports) if ticket_href in ('report', 'query') \ or as_int(ticket_href, 0) in reports: req.session[key] = req.args[key] else: add_warning(req, Markup(tag.span(Markup(_( "%(report)s is not a valid report", report=tag.b(ticket_href) ))))) else: req.session[key] = req.args[key]
def pre_process_request(self, req, handler): # TODO Ugly set of conditions, make the if beautiful if req.path_info.startswith('/browser') and req.method == 'POST' \ and ('bsop_upload_file' in req.args or 'bsop_mvdel_op' in req.args or 'bsop_create_folder_name' in req.args): req.perm.require('REPOSITORY_MODIFY') self.log.debug('Intercepting browser POST') # Dispatch to private handlers based on which form submitted # The handlers perform a redirect, so don't return the handler try: if 'bsop_upload_file' in req.args: self._upload_request(req, handler) elif 'bsop_mvdel_op' in req.args: self._move_delete_request(req, handler) elif 'bsop_create_folder_name' in req.args: self._create_path_request(req, handler) except PermissionError, e: add_warning(req, "Permission denied") req.redirect(req.href(req.path_info)) except RequestDone: # Raised from redirect in successful operations above, we don't want it caught # by the below general except. raise
def post_process_request(self, req, template, data, content_type): """Do any post-processing the request might need; typically adding values to the template `data` dictionary, or changing template or mime type. `data` may be update in place. Always returns a tuple of (template, data, content_type), even if unchanged. Note that `template`, `data`, `content_type` will be `None` if: - called when processing an error page - the default request handler did not return any result (Since 0.11) """ if template != "query.html": return (template, data, content_type) geoticket = self.geoticket() location = req.args.get("center_location", "").strip() lat = lon = None if location: try: location, (lat, lon) = geoticket.geolocate(location) except GeolocationException, e: add_script(req, "geoticket/js/query_location_filler.js") add_warning(req, Markup(e.html()))
def post_process_request(self, req, template, data, content_type): """Do any post-processing the request might need; typically adding values to the template `data` dictionary, or changing template or mime type. `data` may be update in place. Always returns a tuple of (template, data, content_type), even if unchanged. Note that `template`, `data`, `content_type` will be `None` if: - called when processing an error page - the default request handler did not return any result (Since 0.11) """ if template == 'ticket.html': geoticket = GeoTicket(self.env) ticket = data['ticket'] message = req.session.pop('geolocation_error', None) if message: add_warning(req, Markup(message)) return (template, data, content_type)
def query(self, req, query="", attribute_handlers=None): """Return a sequence of (resource, tags) tuples matching a query. Query syntax is described in tractags.query. :param attribute_handlers: Register additional query attribute handlers. See Query documentation for more information. """ def realm_handler(_, node, context): return query.match(node, [context.realm]) all_attribute_handlers = {"realm": realm_handler} all_attribute_handlers.update(attribute_handlers or {}) if re.search(r"(expression|tagspace|tagspaces|operation|showheadings" "|expression)=", query): message = Markup( "You seem to be using an old Tag query. " 'Try using the <a href="%s">new syntax</a> in your ' "<strong>ListTagged</strong> macro.", req.href("tags"), ) add_warning(req, message) query = Query(query, attribute_handlers=all_attribute_handlers) query_tags = set(query.terms()) for provider in self.tag_providers: for resource, tags in provider.get_tagged_resources(req, query_tags): if query(tags, context=resource): yield resource, tags
def post_process_request(self, req, template, data, content_type): if (template, data) != (None, None) or \ sys.exc_info() != (None, None, None): try: theme = self.system.theme except ThemeNotFound, e: add_warning(req, "Unknown theme %s configured. Please check " "your trac.ini. You may need to enable " "the theme\'s plugin." % e.theme_name) else: if theme and 'css' in theme: add_stylesheet(req, 'theme/'+theme['css']) if theme and 'template' in theme: req.chrome['theme'] = os.path.basename(theme['template']) if theme and theme.get('disable_trac_css'): links = req.chrome.get('links') if links and 'stylesheet' in links: for i, link in enumerate(links['stylesheet']): if link.get('href','') \ .endswith('common/css/trac.css'): del links['stylesheet'][i] break if theme: # Template overrides (since 2.2.0) overrides = self._get_template_overrides(theme) template, modifier = overrides.get(template, (template, None)) if modifier is not None: modifier(req, template, data, content_type) if self.custom_css: add_stylesheet(req, '/themeengine/theme.css')
def _add_organization(self, req, group_store): req.perm.require('PERMISSION_GRANT') group_name = req.args.get('group') organization = req.args.get('organization') try: group_store.add_organization_to_group(organization, group_name) add_notice( req, _('Organization %(organization)s added to group %(group)s', group=group_name, organization=organization)) except ValueError: add_warning( req, _('Organization %(organization)s already exists in group %(group)s', group=group_name, organization=organization))
def _save_ticket_changes(self, req, selected_tickets, new_values, comment, action): """Save all of the changes to tickets.""" when = datetime.now(utc) list_fields = self._get_list_fields() with self.env.db_transaction as db: for id in selected_tickets: t = Ticket(self.env, int(id)) _values = new_values.copy() for field in list_fields: if field in new_values: old = t.values[field] if field in t.values else '' new = new_values[field] mode = req.args.get('batchmod_value_' + field + '_mode') new2 = req.args.get( 'batchmod_value_' + field + '_secondary', '') _values[field] = self._change_list( old, new, new2, mode) controllers = list(self._get_action_controllers( req, t, action)) for controller in controllers: _values.update( controller.get_ticket_changes(req, t, action)) t.populate(_values) t.save_changes(req.authname, comment, when=when) for controller in controllers: controller.apply_action_side_effects(req, t, action) try: tn = BatchTicketNotifyEmail(self.env) tn.notify(selected_tickets, new_values, comment, action, req.authname) except Exception as e: self.log.error( "Failure sending notification on ticket batch" "change: %s", exception_to_unicode(e)) add_warning( req, tag_( "The changes have been saved, but an " "error occurred while sending " "notifications: %(message)s", message=to_unicode(e)))
def _send_response(self, req, data): """Send the response.""" # look for warnings if not len(self.providers): add_warning( req, _('No advanced search providers found. ' + 'You must register a search backend.')) if data.get('results') and not len(data['results']): add_warning(req, _('No results.')) add_stylesheet(req, 'common/css/search.css') add_stylesheet(req, 'advsearch/css/advsearch.css') add_stylesheet(req, 'advsearch/css/pikaday.css') add_script(req, 'advsearch/js/advsearch.js') add_script(req, 'advsearch/js/pikaday.js') return 'advsearch.html', data, None
def _reset_password(self, req, username, email): """Store a new, temporary password on admin or user request. This method is used by acct_mgr.admin.AccountManagerAdminPanel too. """ acctmgr = self.acctmgr new_password = self._random_password try: self.store.set_password(username, new_password) acctmgr._notify('password_reset', username, email, new_password) except NotificationError, e: msg = _("Error raised while sending a change notification.") if req.path_info.startswith('/admin'): msg += _("You'll get details with TracLogging enabled.") else: msg += _("You should report that issue to a Trac admin.") add_warning(req, msg) self.log.error("Unable to send password reset notification: %s", exception_to_unicode(e, traceback=True))
def merge_to_category(self, req): req.perm.require('TRAC_ADMIN') # possible params involved in req: target_category_name, target_category_id target_category = self._translate_req_to_category(req, 'target') # possible params involved in req: merged_category_id, merged_category_name merged_category = self._translate_req_to_category(req, 'merged') if not target_category or not merged_category: add_warning(req, _('Invalid merge category parameters: No corresponding category found.')) return try: self.categorystore.merge_category_to_category(merged_category.category_id, target_category.category_id) add_notice(req, _('Category %(merged)s has been merged to %(target)s.', merged = merged_category.name, target = target_category.name)) except Exception as e: add_warning(req, _('Category %(what)s was not merged. ', what=merged_category.name) + _(str(e)))
def _save_ticket_changes(self, req, selected_tickets, new_values, comment, action): """Save changes to tickets.""" valid = True for manipulator in self.ticket_manipulators: if hasattr(manipulator, 'validate_comment'): for message in manipulator.validate_comment(req, comment): valid = False add_warning(req, tag_("The ticket comment is invalid: " "%(message)s", message=message)) tickets = [] for id_ in selected_tickets: t = Ticket(self.env, id_) values = self._get_updated_ticket_values(req, t, new_values) for ctlr in self._get_action_controllers(req, t, action): values.update(ctlr.get_ticket_changes(req, t, action)) t.populate(values) for manipulator in self.ticket_manipulators: for field, message in manipulator.validate_ticket(req, t): valid = False if field: add_warning(req, tag_("The ticket field %(field)s is " "invalid: %(message)s", field=tag.strong(field), message=message)) else: add_warning(req, message) tickets.append(t) if not valid: return when = datetime_now(utc) with self.env.db_transaction: for t in tickets: t.save_changes(req.authname, comment, when=when) for ctlr in self._get_action_controllers(req, t, action): ctlr.apply_action_side_effects(req, t, action) event = BatchTicketChangeEvent(selected_tickets, when, req.authname, comment, new_values, action) try: NotificationSystem(self.env).notify(event) except Exception as e: self.log.error("Failure sending notification on ticket batch" "change: %s", exception_to_unicode(e)) add_warning(req, tag_("The changes have been saved, but an error " "occurred while sending notifications: " "%(message)s", message=to_unicode(e)))
def post_process_request(self, req, template, data, mimetype): if self.delegate: add_link(req, 'openid.delegate', self.delegate) server = self.server if not server: for id_regex, server_href in self.known_servers.iteritems(): if id_regex.search(self.delegate): server = server_href break else: add_warning(req, 'OpenID for identity %s unknown.', self.delegate) return template, data, mimetype add_link(req, 'openid.server', server) elif 'TRAC_ADMIN' in req.perm: # Warn admins if the plugin is enabled, but not configured add_warning(req, 'No OpenID identity specified for delegation.') return template, data, mimetype
def _upload_request(self, req, handler): self.log.debug('Handling file upload for "%s"', req.authname) # Retrieve uploaded file upload_file = req.args['bsop_upload_file'] # Retrieve filename, normalize, use to check a file was uploaded # Filename checks adapted from trac/attachment.py filename = getattr(upload_file, 'filename', '') filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')) filename = filename.replace('\\', '/').replace(':', '/') filename = posixpath.basename(filename) if not filename: raise TracError('No file uploaded') # Check size of uploaded file, accepting 0 to max_upload_size bytes file_data = upload_file.value # Alternatively .file for file object file_size = len(file_data) if self.max_upload_size > 0 and file_size > self.max_upload_size: raise TracError('Uploaded file is too large, ' 'maximum upload size: %s' % pretty_size(self.max_upload_size)) self.log.debug('Received file %s with %i bytes', filename, file_size) commit_msg = req.args.get('bsop_upload_commit') self.log.debug('Opening repository for file upload') reponame, repos, path = _get_repository(self.env, req) try: repos_path = repos.normalize_path('/'.join([path, filename])) self.log.debug('Writing file %s to %s in %s', filename, repos_path, reponame) svn_writer = SubversionWriter(self.env, repos, req.authname) rev = svn_writer.put_content(repos_path, file_data, commit_msg) add_notice( req, _("Uploaded %s, creating revision %s.") % (filename, rev)) except Exception, e: self.log.exception("Failed when uploading file %s" % filename) add_warning(req, _("Failed to upload file: %s") % e)
def _do_delete(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') retarget_to = req.args.get('target') or None # Don't translate ticket comment (comment:40:ticket:5658) retargeted_tickets = \ milestone.move_tickets(retarget_to, req.authname, "Ticket retargeted after milestone deleted") milestone.delete() add_notice( req, _('The milestone "%(name)s" has been deleted.', name=milestone.name)) if retargeted_tickets: add_notice( req, _( 'The tickets associated with milestone ' '"%(name)s" have been retargeted to milestone ' '"%(retarget)s".', name=milestone.name, retarget=retarget_to)) new_values = {'milestone': retarget_to} comment = _("Tickets retargeted after milestone deleted") event = BatchTicketChangeEvent(retargeted_tickets, None, req.authname, comment, new_values, None) try: NotificationSystem(self.env).notify(event) except Exception as e: self.log.error( "Failure sending notification on ticket batch " "change: %s", exception_to_unicode(e)) add_warning( req, tag_( "The changes have been saved, but an " "error occurred while sending " "notifications: %(message)s", message=to_unicode(e))) req.redirect(req.href.roadmap())
def render_admin_panel(self, req, cat, page, path_info): req.perm.require("TRAC_ADMIN") arch = ProjectArchive() projects = arch.list() if req.method == 'POST': archived_project_id = int(req.args.get('archived_project_id', 0)) action = req.args.get('action', 'noop') try: if action == 'restore': if arch.restore(archived_project_id, self.env): add_notice(req, _('Restored project successfully')) elif action == 'remove': if arch.remove(archived_project_id): add_notice(req, _('Removed project successfully')) elif action == 'remove_expired': if arch.remove_expired(): add_notice(req, _('Removed expired projects successfully')) else: add_warning(req, _('Unknown action')) except Exception: add_warning(req, _('Failed to complete the action')) self.log.exception('Failed to {0} for project {1}'.format( action, archived_project_id)) # After post method, redirect back to listing return req.redirect(req.href('/admin/projects/prjarchive')) add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_project_archive.js') return 'multiproject_admin_project_archive.html', { 'projects': projects }
def _add_path(self, req): path = req.args.get('path') repository = None tmppath = req.args.get('path') if ":" in tmppath: repository, path = tmppath.split(":") repository = repository.strip() path = path.strip() else: repository = self.project_repos[0] path = tmppath.strip() if not path: add_warning(req, "Path not specified") return {} try: self.authz.add_path(Path(path, [], repository)) return {} except Exception, e: return {'addpath_error' : e}
def _save_config(self, req, notices=None): """Try to save the config, and display either a success notice or a failure warning. """ try: self.cronconf.save() if notices is None: notices = [_('Your changes have been saved.')] for notice in notices: add_notice(req, notice) except Exception, e: self.env.log.error('Error writing to trac.ini: %s', exception_to_unicode(e)) add_warning( req, _('Error writing to trac.ini, make sure it is ' 'writable by the web server. Your changes have ' 'not been saved.'))
def pre_process_request(self, req, handler): if not req.session.authenticated: # Permissions for anonymous users remain unchanged. return handler if AccountManager(self.env).verify_email and handler is not self and \ 'email_verification_token' in req.session and \ not req.perm.has_permission('ACCTMGR_ADMIN'): # TRANSLATOR: Your permissions have been limited until you ... link = tag.a(_("verify your email address"), href=req.href.verify_email()) # TRANSLATOR: ... verify your email address chrome.add_warning( req, Markup( tag.span( Markup( _("Your permissions have been limited until you %(link)s.", link=link))))) req.perm = perm.PermissionCache(self.env, 'anonymous') return handler
def process_request(self, req): """Process the HTTP Requests and validate parameters, at least basically, than send a Command Request to a Controller. The response has to be rendered according to the view needs.""" try: handler = self.get_handler(req) if handler is not None: return self._call_filters_and_handler(req, handler) else: raise TracError('No handler found for method %s' % req.method) except ICommand.NotValidError, e: chrome.add_warning(req, unicode(e)) # not that we update the data, so that the set value are # kept safe for re-displaying a page correctly data = self.get_data_from_session(req) data.update({'error': unicode(e)}) # This will allow to show the wrong field in a different # color or mark them as errors data.update({'errors': e.command_attributes.keys()}) return self.respond(data)
def pre_process_request(self, req, handler): if isinstance(handler, RegistrationModule): if req.method == 'POST' and req.args.get( 'action') == 'create' and req.args.get('question').strip( ).lower() != self.answer.strip().lower(): add_warning(req, self.hint) req.environ['REQUEST_METHOD'] = 'GET' req.args.pop('password', None) req.args.pop('password_confirm', None) # Admin Configuration if req.path_info.startswith('/admin/accounts/config') and \ req.method == 'POST': self.config.set('registerquestion', 'question', req.args.get('question')) self.config.set('registerquestion', 'answer', req.args.get('answer')) self.config.set('registerquestion', 'hint', req.args.get('hint')) self.config.save() return handler
def render_admin_panel(self, req, cat, page, path_info): wikipages = urllib.unquote_plus(req.args.get('wikipages', '')) parts = wikipages.splitlines() data = { 'find': urllib.unquote_plus(req.args.get('find', '')), 'replace': urllib.unquote_plus(req.args.get('replace', '')), 'wikipages': parts, 'redir': req.args.get('redirect', '') == '1', } if req.method == 'POST': # Check that required fields are filled in. if not data['find']: add_warning(req, 'The Find field was empty. Nothing was changed.') if not data['wikipages'] or not data['wikipages'][0]: add_warning( req, 'The Wiki pages field was empty. Nothing was changed.') # Replace the text if the find and wikipages fields have been input. if data['find'] and data['wikipages'] and data['wikipages'][0]: add_notice( req, 'Replaced "%s" with "%s". See the timeline for modified pages.' % (data['find'], data['replace'])) wiki_text_replace(self.env, data['find'], data['replace'], data['wikipages'], req.authname, req.remote_addr, debug=self.log.debug) # Reset for the next display data['find'] = '' data['replace'] = '' data['wikipages'] = '' return 'admin_wikireplace.html', data
def _process_remove_request(self, req, data): """Remove an existing repository.""" repo = self._get_checked_repository(req, req.args.get('reponame')) open_ticket = None with self.env.db_transaction as db: tickets = db("""SELECT ticket FROM ( SELECT src.ticket, src.value as srcrepo, dst.value as dstrepo FROM ticket_custom AS src JOIN ticket_custom AS dst ON (src.ticket = dst.ticket) WHERE src.name = 'pr_srcrepo' AND dst.name = 'pr_dstrepo') WHERE srcrepo = %d OR dstrepo = %d """ % (repo.id, repo.id)) for values in tickets: (id,) = values ticket = Ticket(self.env, id) if ticket['status'] != 'closed': open_ticket = id break if open_ticket: link = tag.a(_("pull request"), href=req.href.ticket(open_ticket)) add_warning(req, tag_('The repository "%(name)s can not be ' 'removed as it is referenced by an open ' '%(link)s.', name=repo.reponame, link=link)) LoginModule(self.env)._redirect_back(req) if req.args.get('confirm'): RepositoryManager(self.env).remove(repo, req.args.get('delete')) add_notice(req, _('The repository "%(name)s" has been removed.', name=repo.reponame)) req.redirect(req.href.repository()) elif req.args.get('cancel'): LoginModule(self.env)._redirect_back(req) data.update({'title': _("Remove Repository"), 'repository': repo})
def post_process_request(self, req, template, data, content_type): if (template, data) != (None, None) or \ sys.exc_info() != (None, None, None): try: theme = self.system.theme except ThemeNotFound, e: add_warning( req, "Unknown theme %s configured. Please check " "your trac.ini. You may need to enable " "the theme\'s plugin." % e.theme_name) else: if theme and 'css' in theme: add_stylesheet(req, 'theme/' + theme['css']) if theme and 'template' in theme: req.chrome['theme'] = os.path.basename(theme['template']) if theme and 'scripts' in theme: for script_def in theme['scripts']: if (isinstance(script_def, tuple) and 1 <= len(script_def) <= 4): add_script(req, *script_def) else: self.log.warning('Bad script def %s for theme %s', script_def, theme['name']) if theme and theme.get('disable_trac_css'): links = req.chrome.get('links') if links and 'stylesheet' in links: for i, link in enumerate(links['stylesheet']): if link.get('href', '') \ .endswith('common/css/trac.css'): del links['stylesheet'][i] break if theme: req.chrome['theme_info'] = theme # Template overrides (since 2.2.0) overrides = self._get_template_overrides(theme) template, modifier = overrides.get(template, (template, None)) if modifier is not None: modifier(req, template, data, content_type) if self.custom_css: add_stylesheet(req, 'site/theme.css')
def _do_account(self, req): if not req.authname or req.authname == 'anonymous': # DEVEL: Shouldn't this be a more generic URL? req.redirect(req.href.wiki()) action = req.args.get('action') delete_enabled = self.acctmgr.supports('delete_user') and \ self.acctmgr.allow_delete_account data = { 'delete_enabled': delete_enabled, 'delete_msg_confirm': _("Are you sure you want to delete your account?"), } force_change_password = req.session.get('force_change_passwd', False) if req.method == 'POST': if action == 'save': data.update(self._do_change_password(req)) if force_change_password: del (req.session['force_change_passwd']) req.session.save() chrome.add_notice( req, Markup( tag.span( tag_( "Thank you for taking the time to update your password." )))) force_change_password = False elif action == 'delete' and delete_enabled: data.update(self._do_delete(req)) else: data.update({'error': 'Invalid action'}) if force_change_password: chrome.add_warning( req, Markup( tag.span( _("You are required to change password because of a recent " "password change request. "), tag.b(_("Please change your password now."))))) return data
def _do_save(self, req, page): if not page.exists: req.perm(page.resource).require('WIKI_CREATE') else: req.perm(page.resource).require('WIKI_MODIFY') if 'WIKI_ADMIN' in req.perm(page.resource): # Modify the read-only flag if it has been changed and the user is # WIKI_ADMIN page.readonly = int('readonly' in req.args) try: page.save(get_reporter_id(req, 'author'), req.args.get('comment'), req.remote_addr) add_notice(req, _("Your changes have been saved in version " "%(version)s.", version=page.version)) req.redirect(get_resource_url(self.env, page.resource, req.href, version=None)) except TracError: add_warning(req, _("Page not modified, showing latest version.")) return self._render_view(req, page)
def _do_delete(self, req): username = req.authname password = req.args.get('password') if not password: add_warning(req, _("Password cannot be empty.")) elif not self.acctmgr.check_password(username, password): add_warning(req, _("Password is incorrect.")) else: try: self.acctmgr.delete_user(username) except NotificationError, e: # User wont care for notification, only care for logging here. self.log.error( "Unable to send account deletion notification: " "%s", exception_to_unicode(e, traceback=True)) # Delete the whole session, since records in session_attribute # would get restored on logout otherwise. req.session.clear() req.session.save() req.redirect(req.href.logout())
def process_post(self, hookname, req): contents = req.args.get('hook-file-contents', None) if contents is None: return if os.linesep != CRLF: contents = os.linesep.join( contents.split(CRLF)) # form contents will have this filename = self.filename(hookname) if not os.path.exists(filename): if not iswritable(filename): add_warning(req, 'File "%s" not writable' % filename) return f = file(filename, 'w') print >> f, contents f.close() try: os.chmod(filename, self.mode) except: # won't work on winblows pass
def process_delete(self, action, node, node_factory, req, timeline_notifier): selected_files = self._get_selected_files(req, node, action == 'delete') successful_files = 0 target_node = None for filename in selected_files: relative_path = os.path.join(node.relative_path, filename) removed_node = MappedFileNode.from_path(relative_path, node_factory, assume_relative_path=True) try: removed_node.remove_by_request(req, timeline_notifier) successful_files += 1 except TracError as e: add_warning(req, _("Could not remove file or folder %(filename)s: ", filename=filename) + str(e)) if len(selected_files) == 1 and successful_files == 1: add_notice(req, _("Removed file %(filename)s successfully", filename=selected_files[0])) elif successful_files and successful_files == len(selected_files): add_notice(req, _("Removed files successfully")) elif successful_files: add_notice(req, _("Removed other files successfully"))
def _create_group(self, req, group_store, perm_sys): req.perm.require('PERMISSION_GRANT') group_perms = req.args.get('group_perms') group_name = req.args.get('group_name') # if only one permission selected if isinstance(group_perms, basestring): group_perms = [group_perms] if not group_name: add_warning(req, _('Invalid group name')) return # trac schema limitation if group_name.isupper(): add_warning(req, _('Group name cannot be in all upper cases')) return valid_perms = perm_sys.get_actions() for perm in group_perms: if perm not in valid_perms: raise TracError('Invalid permission %s' % perm) try: group_store.grant_permission_to_group(group_name, perm) except InvalidPermissionsState, e: add_warning( req, _('Unable to add permission %(perm)s to group %(group)s. %(reason)s', perm=perm, group=group_name, reason=e.message))
def _do_save(self, req, user): """ Update user information into database """ userstore = get_userstore() if not req.args.get('mail'): add_warning(req, _('User should have an e-mail address')) return user user.mail = req.args.get('mail') if not req.args.get('lastName'): add_warning(req, _('Last name required')) return user # NOTE: Values are escaped in userstore update user.lastName = req.args.get('lastName') user.givenName = req.args.get('givenName') user.mobile = req.args.get('mobile') if userstore.updateUser(user): user = userstore.getUser(user.username) add_notice(req, _('Updated user settings')) if req.args.get( 'approve') == 'on' and user.status == user.STATUS_INACTIVE: user.activate() add_notice(req, _("Your account is now activated.")) return user add_warning(req, _('Failed to update user')) return user
def _validate(self, req, page): valid = True # Validate page size if len(req.args.get('text', '')) > self.max_size: add_warning( req, _( 'The wiki page is too long (must be less ' 'than %(num)s characters)', num=self.max_size)) valid = False # Give the manipulators a pass at post-processing the page for manipulator in self.page_manipulators: for field, message in manipulator.validate_wiki_page(req, page): valid = False if field: add_warning( req, tag_( "The Wiki page field %(field)s" " is invalid: %(message)s", field=tag.strong(field), message=message)) else: add_warning( req, tag_("Invalid Wiki page: %(message)s", message=message)) return valid
def _do_rename(self, req, page): if page.readonly: req.perm(page.resource).require('WIKI_ADMIN') else: req.perm(page.resource).require('WIKI_RENAME') if 'cancel' in req.args: req.redirect(get_resource_url(self.env, page.resource, req.href)) old_name, old_version = page.name, page.version new_name = req.args.get('new_name', '') new_name = re.sub(r'/{2,}', '/', new_name.strip('/')) redirect = req.args.get('redirect') # verify input parameters warn = None if not new_name: warn = _("A new name is mandatory for a rename.") elif not validate_page_name(new_name): warn = _("The new name is invalid (a name which is separated " "with slashes cannot be '.' or '..').") elif new_name == old_name: warn = _("The new name must be different from the old name.") elif WikiPage(self.env, new_name).exists: warn = _("The page %(name)s already exists.", name=new_name) if warn: add_warning(req, warn) return self._render_confirm_rename(req, page, new_name) with self.env.db_transaction as db: page.rename(new_name) if redirect: redirection = WikiPage(self.env, old_name, db=db) redirection.text = _('See [wiki:"%(name)s"].', name=new_name) author = get_reporter_id(req) comment = u'[wiki:"%s@%d" %s] \u2192 [wiki:"%s"].' % ( new_name, old_version, old_name, new_name) redirection.save(author, comment, req.remote_addr) req.redirect(req.href.wiki(old_name if redirect else new_name))
def create_failure(self, req, msg): author = get_context(req)['author'] data = {} data['url'] = ProjectListModule.url data['home'] = req.base_path data['user'] = author data['vcslist'] = self._list_enabled_vcs() data['prj_is_public'] = sanitize_html( req.args.get('prj_is_public', 'off')) # Use system warning to notify user add_warning(req, msg) for key in req.args: if key in [ 'prj_long_name', 'prj_short_name', 'prj_description', 'vcstype', '_project_' ]: data[key] = sanitize_html(req.args[key]) return 'create_form.html', data, None
def _check_dir(self, req, dir): """Check that a repository directory is valid, and add a warning message if not. """ if not os.path.isabs(dir): add_warning( req, _('The repository directory must be an absolute path.')) return False prefixes = [ os.path.join(self.env.path, prefix) for prefix in self.allowed_repository_dir_prefixes ] if prefixes and not any( util.is_path_below(dir, prefix) for prefix in prefixes): add_warning( req, _( 'The repository directory must be located below one of the following directories: ' '%(dirs)s', dirs=', '.join(prefixes))) return False return True
def render_admin_panel(self, req, cat, page, version): req.perm.require('TAGS_ADMIN') tag_system = TagSystem(self.env) all_realms = tag_system.get_taggable_realms(req.perm) # Check request for enabled filters, or use default. if not [r for r in all_realms if r in req.args]: for realm in all_realms: req.args[realm] = 'on' checked_realms = [r for r in all_realms if r in req.args] data = dict(checked_realms=checked_realms, tag_realms=list(dict(name=realm, checked=realm in checked_realms) for realm in all_realms)) if req.method == 'POST': # Replace Tag allow_delete = req.args.get('allow_delete') new_tag = req.args.get('tag_new_name').strip() new_tag = not new_tag == u'' and new_tag or None if not (allow_delete or new_tag): add_warning(req, _("Selected current tag(s) and either " "new tag or delete approval are required")) else: comment = req.args.get('comment', u'') old_tags = req.args.getlist('tag_name') if old_tags: tag_system.replace_tag(req, old_tags, new_tag, comment, allow_delete, filter=checked_realms) data['selected'] = new_tag req.redirect(req.href.admin('tags', 'replace')) query = ' or '.join('realm:%s' % r for r in checked_realms) all_tags = sorted(tag_system.get_all_tags(req, query)) data['tags'] = all_tags chrome = Chrome(self.env) chrome.add_textarea_grips(req) return 'admin_tag_change.html', data, {'domain': 'tractags'}