def __init__(self, message, *args, **kwargs): """TracError sub-class with extended i18n support. It eases error initialization with messages optionally including arguments meant for string substitution after deferred translation. """ title = N_("Registration Error") tb = 'show_traceback' # Care for the 2nd TracError standard keyword argument only. show_traceback = tb in kwargs and kwargs.pop(tb, False) TracError.__init__(self, message, title, show_traceback) self.msg_args = args
def get_changes(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=0): # TODO: handle renames/copies, ignore_ancestry old_path = self.normalize_path(old_path) new_path = self.normalize_path(new_path) if old_path != new_path: raise TracError(_("Not supported in git_fs")) old_rev = self.normalize_rev(old_rev) new_rev = self.normalize_rev(new_rev) if old_rev == new_rev: return def get_tree(rev): results = self.git.ls_tree(rev, target_path, recursive=True) return dict((result[4], result) for result in results) def is_dir(mode): return mode.startswith('04') target_path = old_path.strip('/') old_tree = get_tree(old_rev) new_tree = get_tree(new_rev) with self.git.get_historian(old_rev, target_path) as old_historian: with self.git.get_historian(new_rev, target_path) as new_historian: for chg in self.git.diff_tree(old_rev, new_rev, target_path): mode1, mode2, obj1, obj2, action, path, path2 = chg kind = Node.DIRECTORY \ if is_dir(mode2) or is_dir(mode1) \ else Node.FILE change = GitChangeset.action_map[action] old_node = self._get_node(path, old_rev, old_tree.get(path, False), old_historian) \ if change != Changeset.ADD else None new_node = self._get_node(path, new_rev, new_tree.get(path, False), new_historian) \ if change != Changeset.DELETE else None yield old_node, new_node, kind, change
def _prepare_sortedby(self, sort): if not sort: return None sortedby = [] for sort_instruction in sort: field = sort_instruction.field order = sort_instruction.order if field.lower() == SCORE: if self._is_desc(order): #We can implement tis later by our own ScoreFacet with # "score DESC" support raise TracError( "Whoosh does not support DESC score ordering.") sort_condition = whoosh.sorting.ScoreFacet() else: sort_condition = whoosh.sorting.FieldFacet( field, reverse=self._is_desc(order)) sortedby.append(sort_condition) return sortedby
def _process_ticket_request(self, req): if not AgiloConfig(self.env).is_agilo_enabled: return super(AgiloTicketModule, self)._process_ticket_request(req) from agilo.scrum.workflow.api import RuleValidationException # Compute the back_to_url self._set_back_to_url(req) # Check if the delete has been called if 'delete' in req.args: # load the ticket, delete it and change the ID to another one before # sending it to trac self._do_delete(req) # Process the Ticket the TRAC way template = data = content_type = None try: template, data, content_type = super( AgiloTicketModule, self)._process_ticket_request(req) except RuleValidationException, e: raise TracError(exception_to_unicode(e))
def _do_hotcopy(self, dest): if os.path.exists(dest): raise TracError( _("hotcopy can't overwrite existing '%(dest)s'", dest=dest)) import shutil # Bogus statement to lock the database while copying files cnx = self.env.get_db_cnx() cursor = cnx.cursor() cursor.execute("UPDATE system SET name=NULL WHERE name IS NULL") try: printout( _('Hotcopying %(src)s to %(dst)s ...', src=self.env.path, dst=dest)) db_str = self.env.config.get('trac', 'database') prefix, db_path = db_str.split(':', 1) if prefix == 'sqlite': # don't copy the journal (also, this would fail on Windows) db = os.path.join(self.env.path, os.path.normpath(db_path)) skip = [db + '-journal', db + '-stmtjrnl'] else: skip = [] try: copytree(self.env.path, dest, symlinks=1, skip=skip) retval = 0 except shutil.Error, e: retval = 1 printerr( _('The following errors happened while copying ' 'the environment:')) for (src, dst, err) in e.args[0]: if src in err: printerr(' %s' % err) else: printerr(" %s: '%s'" % (err, src)) finally: # Unlock database cnx.rollback() printout(_("Hotcopy done.")) return retval
def insert(self, db=None): """Insert a new component. :since 1.0: the `db` parameter is no longer needed and will be removed in version 0.14 """ assert not self.exists, "Cannot insert existing component" self.name = simplify_whitespace(self.name) if not self.name: raise TracError(_("Invalid component name.")) with self.env.db_transaction as db: self.env.log.debug("Creating new component '%s'", self.name) db( """INSERT INTO component (name,owner,description) VALUES (%s,%s,%s) """, (self.name, self.owner, self.description)) self._old_name = self.name TicketSystem(self.env).reset_ticket_fields()
def insert(self): """Insert a new milestone. """ self.name = simplify_whitespace(self.name) if not self.name: raise TracError(_("Invalid milestone name.")) with self.env.db_transaction as db: self.env.log.debug("Creating new milestone '%s'", self.name) db( """INSERT INTO milestone (name, due, completed, description) VALUES (%s,%s,%s,%s) """, (self.name, to_utimestamp(self.due), to_utimestamp(self.completed), self.description)) self.checkin() TicketSystem(self.env).reset_ticket_fields() for listener in TicketSystem(self.env).milestone_change_listeners: listener.milestone_created(self)
def get_db_version(self, db): """Returns the normalized db version (integer). This method can convert the decimal numbers from Agilo 0.6 to integer.""" fetch_version = super(AgiloInit, self)._fetch_db_version db_version = fetch_version(db, self.name) if db_version == 0: # Agilo versions before 0.7 had different database versions with # floating point old_version = fetch_version(db, name='agilo-types') if old_version == '1.2': db_version = 1 elif old_version != 0: msg = _('Unknown version for agilo-types: %s') % old_version raise TracError(msg) elif db_version == '0.7': db_version = 2 # 'Modern' Agilo versions like 0.7 just return something like '3'. db_version = int(db_version) return db_version
def _prepare_fields_and_view(self): self._add_views_selector() self.view = self._get_view() if self.view: self.data[self.DATA_VIEW] = self.view fields_to_select = None if self.view in self.VIEWS_WITH_KNOWN_FIELDS: if self.active_participant: fields_in_view = self.active_participant.\ get_default_view_fields(self.view) elif self.view == self.DATA_VIEW_GRID: fields_in_view = self.all_grid_fields else: raise TracError("Unsupported view: %s" % self.view) self.data[self.DATA_HEADERS] = [ self._create_headers_item(field) for field in fields_in_view ] fields_to_select = self._add_obligatory_fields(fields_in_view) return fields_to_select
def needs_upgrade(self): """Return whether the environment needs to be upgraded.""" for participant in self.setup_participants: try: with self.component_guard(participant, reraise=True): if participant.environment_needs_upgrade(): self.log.warning( "Component %s requires an environment upgrade", participant) return True except Exception as e: raise TracError( _( "Unable to check for upgrade of " "%(module)s.%(name)s: %(err)s", module=participant.__class__.__module__, name=participant.__class__.__name__, err=exception_to_unicode(e))) return False
def insert(self, db=None): """Insert a new version. :since 1.0: the `db` parameter is no longer needed and will be removed in version 1.1.1 """ assert not self.exists, "Cannot insert existing version" self.name = simplify_whitespace(self.name) if not self.name: raise TracError(_("Invalid version name.")) with self.env.db_transaction as db: self.env.log.debug("Creating new version '%s'", self.name) db("INSERT INTO version (name,time,description) VALUES (%s,%s,%s)", (self.name, to_utimestamp(self.time), self.description)) self._old_name = self.name TicketSystem(self.env).reset_ticket_fields() ResourceSystem(self.env).resource_created(self)
def upgrade_environment(self, db=None): """Each schema version should have its own upgrade module, named upgrades/dbN.py, where 'N' is the version number (int). """ db_mgr = DatabaseManager(self.env) schema_ver = self.get_schema_version() with self.env.db_transaction as db: # Is this a new installation? if not schema_ver: # Perform a single-step install: Create plugin schema and # insert default data into the database. connector = db_mgr.get_connector()[0] for table in db_default.schema: for stmt in connector.to_sql(table): db(stmt) for table, cols, vals in db_default.get_data(db): db.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s'] * len(cols))), vals) else: # Perform incremental upgrades. for i in range(schema_ver + 1, db_default.schema_version + 1): name = 'db%i' % i try: upgrades = __import__('tractags.upgrades', globals(), locals(), [name]) script = getattr(upgrades, name) except AttributeError: raise TracError(_("No upgrade module for version " "%(num)i (%(version)s.py)", num=i, version=name)) cursor = db.cursor() script.do_upgrade(self.env, i, cursor) db("""UPDATE system SET value=%s WHERE name='tags_version' """, (db_default.schema_version,)) self.log.info("Upgraded TracTags db schema from version %d to %d", schema_ver, db_default.schema_version) TicketTagProvider(self.env)._fetch_tkt_tags() self.log.info("Synchronized ticket attributes to tags table")
def do_deploy(self, line): argv = self.arg_tokenize(line) if not argv[0]: self.do_help('deploy') return target = os.path.normpath(argv[0]) if os.path.exists(target): raise TracError('Destination already exists. Remove and retry.') chrome_target = os.path.join(target, 'htdocs') script_target = os.path.join(target, 'cgi-bin') # Copy static content os.makedirs(target) os.makedirs(chrome_target) from trac.web.chrome import Chrome env = self.env_open() printout(_("Copying resources from:")) for provider in Chrome(env).template_providers: paths = list(provider.get_htdocs_dirs()) if not len(paths): continue printout(' %s.%s' % (provider.__module__, provider.__class__.__name__)) for key, root in paths: source = os.path.normpath(root) printout(' ', source) if os.path.exists(source): dest = os.path.join(chrome_target, key) copytree(source, dest, overwrite=True) # Create and copy scripts os.makedirs(script_target) printout(_("Creating scripts.")) data = {'env': env, 'executable': sys.executable} for script in ('cgi', 'fcgi', 'wsgi'): dest = os.path.join(script_target, 'trac.'+script) template = Chrome(env).load_template('deploy_trac.'+script, 'text') stream = template.generate(**data) out = open(dest, 'w') stream.render('text', out=out) out.close()
def get_repository(self, repos_type, repos_dir, params): t = datetime(2017, 3, 31, 12, 34, 56, tzinfo=utc) def get_changeset(rev): return Mock(Changeset, repos, rev, 'message', 'author', t) def get_node(path, rev): if 'missing' in path: raise NoSuchNode(path, rev) basename = posixpath.basename(path) if 'file' in basename: kind = Node.FILE entries = () else: kind = Node.DIRECTORY if 'dir' in basename: entries = ['file.txt'] else: entries = ['dir1', 'dir2'] entries = [posixpath.join(path, entry) for entry in entries] content = 'Contents for %s' % to_utf8(path) node = Mock(Node, repos, path, rev, kind, created_path=path, created_rev=rev, get_entries=lambda: iter(get_node(entry, rev) for entry in entries), get_properties=lambda: {}, get_content=lambda: io.BytesIO(content), get_content_length=lambda: len(content), get_content_type=lambda: 'application/octet-stream', get_last_modified=lambda: t) return node if params['name'] == 'raise': raise TracError("") else: repos = Mock(Repository, params['name'], params, self.log, get_youngest_rev=lambda: 1, get_changeset=get_changeset, get_node=get_node, previous_rev=lambda rev, path='': None, next_rev=lambda rev, path='': None) return repos
def save_metadata(self, metadata): """Save the repository metadata.""" with self.env.db_transaction as db: invalidate = False # -- check that we're populating the cache for the correct # repository repository_dir = metadata.get(CACHE_REPOSITORY_DIR) if repository_dir: # directory part of the repo name can vary on case insensitive # fs if os.path.normcase(repository_dir) \ != os.path.normcase(self.name): self.log.info("'repository_dir' has changed from %r to %r", repository_dir, self.name) raise TracError(_("The repository directory has changed, " "you should resynchronize the " "repository with: trac-admin $ENV " "repository resync '%(reponame)s'", reponame=self.reponame or '(default)')) elif repository_dir is None: # self.log.info('Storing initial "repository_dir": %s', self.name) db("""INSERT INTO repository (id, name, value) VALUES (%s, %s, %s) """, (self.id, CACHE_REPOSITORY_DIR, self.name)) invalidate = True else: # 'repository_dir' cleared by a resync self.log.info('Resetting "repository_dir": %s', self.name) db("UPDATE repository SET value=%s WHERE id=%s AND name=%s", (self.name, self.id, CACHE_REPOSITORY_DIR)) invalidate = True # -- insert a 'youngeset_rev' for the repository if necessary if CACHE_YOUNGEST_REV not in metadata: db("""INSERT INTO repository (id, name, value) VALUES (%s, %s, %s) """, (self.id, CACHE_YOUNGEST_REV, '')) invalidate = True if invalidate: del self.metadata
def add_config_platform_rev_index_to_build(env, db): """Adds a unique index on (config, platform, rev) to the bitten_build table. Also drops the old index on bitten_build that serves no real purpose anymore.""" # check for existing duplicates duplicates_cursor = db.cursor() build_cursor = db.cursor() duplicates_cursor.execute("SELECT config, rev, platform FROM bitten_build GROUP BY config, rev, platform HAVING COUNT(config) > 1") duplicates_exist = False for config, rev, platform in duplicates_cursor.fetchall(): if not duplicates_exist: duplicates_exist = True print "\nConfig Name, Revision, Platform :: [<list of build ids>]" print "--------------------------------------------------------" build_cursor.execute("SELECT id FROM bitten_build WHERE config='%s' AND rev='%s' AND platform='%s'" % (config, rev, platform)) build_ids = [row[0] for row in build_cursor.fetchall()] print "%s, %s, %s :: %s" % (config, rev, platform, build_ids) if duplicates_exist: print "--------------------------------------------------------\n" print "Duplicate builds found. You can obtain help on removing the" print "builds you don't want by reading the Bitten upgrade" print "documentation at:" print "http://bitten.edgewall.org/wiki/Documentation/upgrade.html" print "Upgrades cannot be performed until conflicts are resolved." print "The upgrade script will now exit with an error:\n" duplicates_cursor.close() build_cursor.close() if not duplicates_exist: cursor = db.cursor() scheme = parse_scheme(env) if scheme == "mysql": # 111 = 333 / len(columns in index) -- this is the Trac default cursor.execute("CREATE UNIQUE INDEX bitten_build_config_rev_platform_idx ON bitten_build (config(111), rev(111), platform)") else: cursor.execute("CREATE UNIQUE INDEX bitten_build_config_rev_platform_idx ON bitten_build (config,rev,platform)") drop_index(env, db, 'bitten_build', 'bitten_build_config_rev_slave_idx') else: raise TracError('')
def _get_type(self, word): # Accept unique abbrevs. of type if not word: return '' if word in self.word2type: return self.word2type[word] type_ = '' for w in self.word2type.iterkeys(): try: if w.startswith(word): t = self.word2type[w] if type_ and type_ != t: return # 2nd found, not unique type_ = t except TypeError as e: raise TracError( _("Invalid argument %(arg)s (%(type)s)", arg=word, type=type(word))) return type_
def get_changeset(self, rev): self._log.debug('get_changeset(%r)' % rev) if isinstance(rev, int): change = rev else: from p4trac.util import toUnicode rev = toUnicode(rev) if rev.startswith(u'@'): rev = rev[1:] try: change = int(rev) except ValueError: raise TracError(u"Invalid changeset number '%s'" % rev) return PerforceChangeset(change, self._repos, self._log, self._job_prefix_length)
def source(self, req, args): arg = re.compile(":|@").split(args) if len(arg) < 2: raise TracError('[[Usage: BibAdd(source:file[@rev])') elif len(arg) == 2: revision = 'latest' else: revision = arg[2] file = arg[1] repos = self.env.get_repository() # load the file from the repository bib = repos.get_node(file, revision) file = bib.get_content() text = file.read() return _extract(text)
def _load(self, db=None): """Try to load the Sprint from the database""" db, handle_ta = get_db_for_write(self.env, db) sql_query = "SELECT date, remaining_time FROM %s" \ " WHERE task_id=%d ORDER BY date DESC" % (BURNDOWN_TABLE, self.task.id) debug(self, "Burndown-SQL Query: %s" % sql_query) try: history = dict() cursor = db.cursor() cursor.execute(sql_query) for row in cursor.fetchall(): timestamp, remaining_time = row history[timestamp] = remaining_time self.loaded = True except Exception, e: error(self, to_unicode(e)) if handle_ta: db.rollback() raise TracError("An error occurred while loading Burndown data: %s" % to_unicode(e))
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 _parse_sort(self, sort_string): if not sort_string: return None sort_terms = sort_string.split(",") sort = [] for term in sort_terms: term = term.strip() if not term: continue term_parts = term.split() parts_count = len(term_parts) if parts_count == 1: sort.append(SortInstruction(term_parts[0], ASC)) elif parts_count == 2: sort.append(SortInstruction(term_parts[0], term_parts[1])) else: raise TracError("Invalid sort term %s " % term) return sort if sort else None
def needs_upgrade(self, version, name='database_version'): """Checks the database version to determine if an upgrade is needed. :param version: the expected integer database version. :param name: the name of the entry in the SYSTEM table that contains the database version. Defaults to `database_version`, which contains the database version for Trac. :return: `True` if the stored version is less than the expected version, `False` if it is equal to the expected version. :raises TracError: if the stored version is greater than the expected version. """ dbver = self.get_database_version(name) if dbver == version: return False elif dbver > version: raise TracError(_("Need to downgrade %(name)s.", name=name)) self.log.info("Need to upgrade %s from %d to %d", name, dbver, version) return True
def default(self, line): try: if not self.__env: self._init_env() if self.needs_upgrade is None: self.needs_upgrade = self.__env.needs_upgrade() except TracError as e: raise AdminCommandError(to_unicode(e)) except Exception as e: raise AdminCommandError(exception_to_unicode(e)) args = self.arg_tokenize(line) if args[0] == 'upgrade': self.needs_upgrade = None elif self.needs_upgrade: raise TracError( _( 'The Trac Environment needs to be upgraded. ' 'Run:\n\n trac-admin "%(path)s" upgrade', path=self.envname)) return self.cmd_mgr.execute_command(*args)
def upgrade_environment(self, db): self.log.debug("Upgrading timeline environment") cursor = db.cursor() # Get current database schema version db_version = self._get_db_version(cursor) # Perform incremental upgrades try: for i in range(db_version + 1, timeline_db_version + 1): script_name = 'db%i' % i module = __import__( 'multiproject.project.files.db.webdav_events_%s' % script_name, globals(), locals(), ['do_upgrade']) module.do_upgrade(self.env, cursor) db.commit() except: raise TracError("Upgrading timeline environment failed") finally: cursor.close()
def upgrade_environment(self, db): if self.found_db_version == 0: self.environment_created() return cursor = db.cursor() for i in range(self.found_db_version + 1, db_default.version + 1): name = 'db%i' % i try: upgrades = __import__('upgrades', globals(), locals(), [name]) script = getattr(upgrades, name) except AttributeError: raise TracError('No upgrade module for %s version %i', db_default.name, i) script.do_upgrade(self.env, i, cursor) cursor.execute('UPDATE system SET value=%s WHERE name=%s', (db_default.version, db_default.name)) db.commit() self.log.info('Upgraded %s database version from %d to %d', db_default.name, i - 1, i)
def update(self, db=None): """Update the milestone. :since 1.0: the `db` parameter is no longer needed and will be removed in version 1.1.1 """ self.name = simplify_whitespace(self.name) if not self.name: raise TracError(_("Invalid milestone name.")) old = self._old.copy() with self.env.db_transaction as db: old_name = old['name'] self.env.log.info("Updating milestone '%s'", self.name) db( """UPDATE milestone SET name=%s, due=%s, completed=%s, description=%s WHERE name=%s """, (self.name, to_utimestamp(self.due), to_utimestamp(self.completed), self.description, old_name)) self.checkin() if self.name != old_name: # Update milestone field in tickets self.env.log.info( "Updating milestone field of all tickets " "associated with milestone '%s'", self.name) db("UPDATE ticket SET milestone=%s WHERE milestone=%s", (self.name, old_name)) TicketSystem(self.env).reset_ticket_fields() # Reparent attachments Attachment.reparent_all(self.env, 'milestone', old_name, 'milestone', self.name) old_values = dict( (k, v) for k, v in old.iteritems() if getattr(self, k) != v) for listener in TicketSystem(self.env).milestone_change_listeners: listener.milestone_changed(self, old_values) ResourceSystem(self.env).resource_changed(self, old_values)
def migrate_logs_to_files(env, db): """Migrates logs that are stored in the bitten_log_messages table into files.""" logs_dir = env.config.get("bitten", "logs_dir", "log/bitten") if not os.path.isabs(logs_dir): logs_dir = os.path.join(env.path, logs_dir) if os.path.exists(logs_dir): print "Bitten log folder %r already exists" % (logs_dir,) print "Upgrade cannot be performed until the existing folder is moved." print "The upgrade script will now exit with an error:\n" raise TracError("") os.makedirs(logs_dir) cursor = db.cursor() message_cursor = db.cursor() update_cursor = db.cursor() cursor.execute("SELECT id FROM bitten_log") for log_id, in cursor.fetchall(): filename = "%s.log" % (log_id,) message_cursor.execute("SELECT message, level FROM bitten_log_message WHERE log=%s ORDER BY line", (log_id,)) full_filename = os.path.join(logs_dir, filename) message_file = codecs.open(full_filename, "wb", "UTF-8") # Note: the original version of this code erroneously wrote to filename + ".level" instead of ".levels", producing unused level files level_file = codecs.open(full_filename + '.levels', "wb", "UTF-8") for message, level in message_cursor.fetchall() or []: message_file.write(to_unicode(message) + "\n") level_file.write(to_unicode(level) + "\n") message_file.close() level_file.close() update_cursor.execute("UPDATE bitten_log SET filename=%s WHERE id=%s", (filename, log_id)) env.log.info("Migrated log %s", log_id) env.log.warning("Logs have been migrated from the database to files in %s. " "Ensure permissions are set correctly on this file. " "Since we presume that the migration worked correctly, " "we are now dropping the bitten_log_message table in the database (aren't you glad you backed up)", logs_dir) cursor.close() cursor = db.cursor() cursor.execute("DROP TABLE bitten_log_message") cursor.close() env.log.warning("We have dropped the bitten_log_message table - you may want to vaccuum/compress your database to save space")
def process_request(self, req): """Override for TicketModule process_request""" ticketid = req.args.get('id') productid = req.args.get('productid', '') if not ticketid: # if /newticket is executed in global scope (from QCT), redirect # the request to /products/<first_product_in_DB>/newticket if not productid and \ not isinstance(self.env, ProductEnvironment): default_product = self.env.config.get('ticket', 'default_product') products = Product.select(self.env, {'fields': ['prefix']}) prefixes = [prod.prefix for prod in products] if not default_product or default_product not in prefixes: default_product = products[0].prefix req.redirect(req.href.products(default_product, 'newticket')) return self._process_newticket_request(req) if req.path_info in ('/newticket', '/products'): raise TracError(_("id can't be set for a new ticket request.")) if isinstance(self.env, ProductEnvironment): ticket = Ticket(self.env, ticketid) if productid and ticket['product'] != productid: msg = "Ticket %(id)s in product '%(prod)s' does not exist." raise ResourceNotFound(_(msg, id=ticketid, prod=productid), _("Invalid ticket number")) return self._process_ticket_request(req) # executed in global scope -> assume ticketid=UID, redirect to product with self.env.db_direct_query as db: rows = db("""SELECT id,product FROM ticket WHERE uid=%s""", (ticketid, )) if not rows: msg = "Ticket with uid %(uid)s does not exist." raise ResourceNotFound(_(msg, uid=ticketid), _("Invalid ticket number")) tid, prefix = rows[0] req.redirect(req.href.products(prefix, 'ticket', tid))
def change_sid(self, new_sid): assert self.req.authname == 'anonymous', \ 'Cannot change ID of authenticated session' assert new_sid, 'Session ID cannot be empty' if new_sid == self.sid: return with self.env.db_transaction as db: if db("SELECT sid FROM session WHERE sid=%s", (new_sid,)): raise TracError(_("Session '%(id)s' already exists. " "Please choose a different session ID.", id=new_sid), _("Error renaming session")) self.env.log.debug("Changing session ID %s to %s", self.sid, new_sid) db("UPDATE session SET sid=%s WHERE sid=%s AND authenticated=0", (new_sid, self.sid)) db("""UPDATE session_attribute SET sid=%s WHERE sid=%s and authenticated=0 """, (new_sid, self.sid)) self.sid = new_sid self.bake_cookie()
def insert(self, db=None): """Insert a new milestone. :since 0.13: the `db` parameter is no longer needed and will be removed in version 0.14 """ self.name = simplify_whitespace(self.name) if not self.name: raise TracError(_("Invalid milestone name.")) with self.env.db_transaction as db: self.env.log.debug("Creating new milestone '%s'", self.name) db("""INSERT INTO milestone (name, due, completed, description) VALUES (%s,%s,%s,%s) """, (self.name, to_utimestamp(self.due), to_utimestamp(self.completed), self.description)) self._to_old() TicketSystem(self.env).reset_ticket_fields() for listener in TicketSystem(self.env).milestone_change_listeners: listener.milestone_created(self)
def _create_user(req, env, check_permissions=True): acctmgr = AccountManager(env) username = acctmgr.handle_username_casing( req.args.get('username').strip()) name = req.args.get('name') email = req.args.get('email').strip() account = {'username' : username, 'name' : name, 'email' : email, } error = TracError('') error.account = account if not username: error.message = _("Username cannot be empty.") raise error # Prohibit some user names that are important for Trac and therefor # reserved, even if they're not in the permission store for some reason. if username in ['authenticated', 'anonymous']: error.message = _("Username %s is not allowed.") % username raise error # NOTE: A user may exist in the 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 obfuscate whether an existing user or group name # was responsible for rejection of this user name. if acctmgr.has_user(username): error.message = _( "Another account or group named %s already exists.") % username raise error # Check whether there is also a user or a group with that name. if check_permissions: # NOTE: We can't use 'get_user_permissions(username)' here # as this always returns a list - even if the user doesn't exist. # In this case the permissions of "anonymous" are returned. # # Also note that we can't simply compare the result of # 'get_user_permissions(username)' to some known set of permission, # i.e. "get_user_permissions('authenticated') as this is always # false when 'username' is the name of an existing permission group. # # And again obfuscate whether an existing user or group name # was responsible for rejection of this username. for (perm_user, perm_action) in \ perm.PermissionSystem(env).get_all_permissions(): if perm_user == username: error.message = _( "Another account or group named %s already exists.") \ % username raise error # 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 containsAny(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), '\'') error.message = tag(_( "The username must not contain any of these characters:"), pretty_blacklist) raise error # Validation of username passed. password = req.args.get('password') if not password: error.message = _("Password cannot be empty.") raise error if password != req.args.get('password_confirm'): error.message = _("The passwords must match.") raise error # Validation of password passed. if if_enabled(EmailVerificationModule) and acctmgr.verify_email: if not email: error.message = _("You must specify a valid email address.") raise error elif not re.match('^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', email, re.IGNORECASE): error.message = _("""The email address specified appears to be invalid. Please specify a valid email address. """) raise error elif acctmgr.has_email(email): error.message = _("""The email address specified is already in use. Please specify a different one. """) raise error # Validation of email address passed. acctmgr.set_password(username, password) # INSERT new sid, needed as foreign key in some db schemata later on, # at least for PostgreSQL. db = env.get_db_cnx() cursor = db.cursor() cursor.execute(""" SELECT COUNT(*) FROM session WHERE sid=%s """, (username,)) exists = cursor.fetchone() if not exists: cursor.execute(""" INSERT INTO session (sid,authenticated,last_visit) VALUES (%s,0,0) """, (username,)) for attribute in ('name', 'email'): value = req.args.get(attribute) if not value: continue set_user_attribute(env, username, attribute, value)
def _do_users(self, req): env = self.env perm = PermissionSystem(env) acctmgr = self.acctmgr acctmod = AccountModule(env) guard = self.guard listing_enabled = acctmgr.supports('get_users') create_enabled = acctmgr.supports('set_password') password_change_enabled = acctmgr.supports('set_password') password_reset_enabled = acctmod.reset_password_enabled delete_enabled = acctmgr.supports('delete_user') verify_enabled = acctmgr.verify_email and \ EmailVerificationModule(env).email_enabled account = dict(email=req.args.get('email', '').strip(), name=req.args.get('name', '').strip(), username=acctmgr.handle_username_casing( req.args.get('username', '').strip())) data = { '_dgettext': dgettext, 'acctmgr': account, 'email_approved': True, 'listing_enabled': listing_enabled, 'create_enabled': create_enabled, 'delete_enabled': delete_enabled, 'verify_enabled': verify_enabled, 'ignore_auth_case': self.config.getbool('trac', 'ignore_auth_case'), 'password_change_enabled': password_change_enabled, 'password_reset_enabled': password_reset_enabled } if req.method == 'GET': if 'user' in req.args.iterkeys(): return self._do_acct_details(req) elif req.args.get('max_per_page'): return self._do_db_cleanup(req) if req.method == 'POST': email_approved = req.args.get('email_approved') # Preserve selection during a series of requests. data['email_approved'] = email_approved if req.args.get('add'): # Add new user account. if create_enabled: # Check request and prime account on success. try: acctmgr.validate_registration(req) # Account email approval for authoritative action. if verify_enabled and email_approved and \ account['email']: set_user_attribute(env, account['username'], 'email_verification_sent_to', account['email']) # User editor form clean-up. data['acctmgr'] = {} except RegistrationError, e: # Attempt deferred translation. message = gettext(e.message) # Check for (matching number of) message arguments # before attempting string substitution. if e.msg_args and \ len(e.msg_args) == len(re.findall('%s', message)): message = message % e.msg_args data['editor_error'] = Markup(message) else: data['editor_error'] = _( "The password store does not support creating users.") elif req.args.get('reset') and req.args.get('sel'): # Password reset for one or more accounts. if password_reset_enabled: sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] for username, name, email in env.get_known_users(): if username in sel: acctmod._reset_password(username, email) else: data['deletion_error'] = _( "The password reset procedure is not enabled.") elif req.args.get('remove') and req.args.get('sel'): # Delete one or more accounts. if delete_enabled: sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] for account in sel: acctmgr.delete_user(account) else: data['deletion_error'] = _( "The password store does not support deleting users.") elif req.args.get('change'): # Change attributes and or password of existing user account. attributes = { 'email': _("Email Address"), 'name': _("Pre-/Surname (Nickname)"), 'password': _("Password") } data['success'] = [] error = TracError('') username = acctmgr.handle_username_casing( req.args.get('username').strip()) try: if not username: error.account = {'username' : username} error.message = _("Username cannot be empty.") raise error if not acctmgr.has_user(username): error.account = {'username' : username} error.message = _("Unknown user %(user)s.", user=username) raise error password = req.args.get('password') if password and (password.strip() != ''): if password_change_enabled: if password != req.args.get('password_confirm'): error.message = _("The passwords must match.") raise error acctmgr.set_password(username, password) data['success'].append(attributes.get('password')) else: data['editor_error'] = _( """The password store does not support changing passwords. """) for attribute in ('name', 'email'): value = req.args.get(attribute, '').strip() if value: set_user_attribute(env, username, attribute, value) data['success'].append(attributes.get(attribute)) # Account email approval for authoritative action. if attribute == 'email' and verify_enabled and \ email_approved: set_user_attribute(env, username, 'email_verification_sent_to', value) # User editor form clean-up on success. data['acctmgr'] = {} except TracError, e: data['editor_error'] = e.message data['acctmgr'] = getattr(e, 'account', '')
def __init__(self, argname, message, title=None, show_traceback=False): message = _("Invalid argument") + " `" + argname + "`. " + message TracError.__init__(self, message, title, show_traceback) self.argname = argname