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 = is_enabled(self.env, 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 filter_stream(self, req, method, filename, stream, data): """Return a filtered Genshi event stream, or the original unfiltered stream if no match. `req` is the current request object, `method` is the Genshi render method (xml, xhtml or text), `filename` is the filename of the template to be rendered, `stream` is the event stream and `data` is the data for the current template. See the Genshi documentation for more information. """ #self.env.log.info(filename) add_captcha = False if data['authname'] == 'anonymous': if self.is_banned(req): self.env.log.debug("%s %s %s%s: IP banned as spammer" % (req.remote_addr, req.remote_user, req.base_path, req.path_info)) stream = tag.label("System offline.") return stream href = req.path_info self.env.log.debug(href) self.env.log.debug(filename) if filename == "ticket.html": if "newticket" in href: add_captcha = 'TICKET_CREATE' in req.perm elif "ticket" in href: add_captcha = 'TICKET_MODIFY' in req.perm or 'TICKET_APPEND' in req.perm elif filename == "wiki_edit.html": add_captcha = 'WIKI_MODIFY' in req.perm if add_captcha: # Insert the math question right before the submit buttons stream = stream | Transformer('//div[@class="buttons"]').before(self.get_content(req)) return stream
def _process_edit(self, req, stream, method, tags): stage = 1 elm = tag.div([tag.label('Tag under: (', tag.a('view all tags', href=req.href.tags()), ')', for_='tags'), tag.br(), tag.input(title='Comma separated list of tags', type='text', id='tags', size='30', name='tags', value=', '.join(tags) ), ], class_='field') for kind, data, pos in stream: yield kind, data, pos if stage == 1 and \ kind is START and \ data[0].localname == 'input' and \ data[1].get('id') == 'comment': stage = 2 elif stage == 2 and \ kind is END and \ data.localname == 'div': for e in elm.generate(): yield e stage = None
def filter_stream(self, req, method, filename, stream, data): if filename.startswith("roadmap"): filter_projects = smp_filter_settings(req, 'roadmap', 'projects') filter = Transformer('//form[@id="prefs"]/fieldset/div[1]') stream = stream | filter.before(tag.label("Filter Projects:")) | filter.before(tag.br()) | filter.before(self._projects_field_input(req, filter_projects)) | filter.before(tag.br()) return stream
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', '') # TRANSLATOR: Hint for visible bot trap registration input field. hint = tag.p(Markup(_( """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 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', '') # TRANSLATOR: Hint for visible bot trap registration input field. hint = tag.p(Markup(_( """Apologies for the inconvenience, but please use the Sugarlabs Wiki, find the page referring to find the gold in the pot, and insert here the four words on the second line, or send mail to systems mailing list.""", 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 filter_stream(self, req, method, filename, stream, data): """ Returns changed stream for `prefs_general.html` template with notification opt-out preference option. `req` is the current request object, `method` is the Genshi render method (xml, xhtml or text), `filename` is the filename of the template to be rendered, `stream` is the event stream and `data` is the data for the current template. """ if filename == 'prefs_general.html' and req.authname != 'anonymous': stream |= Transformer('.//table').append( tag.tr( tag.th( tag.label('Ticket notifications opt-out:', **{'for': 'ticket-notification-optout'}), ), tag.td( tag.input(type="hidden", name="ticket-notification-optout_cb", value=""), tag.input(type="checkbox", id="ticket-notification-optout", name="ticket-notification-optout", checked=req.session.get('ticket-notification-optout') or None), ), **{'class': 'field'} ), ) return stream
def filter_stream(self, req, method, filename, stream, data): # check preconditions if filename != 'ticket.html': return stream transformer = Transformer() # build 'Hide these fields' Area hide_names = req.session.get('hidefieldchanges', []) hide_fields = [] for field in data['fields']: name = field['name'] checkbox = name in hide_names \ and tag.input(type='checkbox', checked=True, name=name) \ or tag.input(type='checkbox', name=name) hide_fields.append(tag.label(checkbox, field['label'])) hide_fields.append(tag.br) hide_fields.append(tag.input(name='submit', type='submit', value='Hide these fields')) transformer = transformer \ .select('//div[@id="changelog"]') \ .before(tag.form(tag.input(value='hide changes', id='hidechangesbutton', type='button', style_='float: right;'), tag.div(hide_fields, style='display: none', id='hidefields'), action='#', class_='inlinebuttons hidechanges')).end() # build 'show all changes' button # showallbutton = tag.input(value='show all', name='showall', class_='showallbutton', type='submit', style_='float: right;') # showallbutton = tag.form(showallbutton, action='#', method='get', class_='inlinebuttons hidechanges') # transformer = transformer.select('//div[@id="changelog"]').before(showallbutton).end() # build 'hide customfield' buttons hidebutton = tag.input(value='hide', name="hide", class_='hidebutton', style_='display: none', type='submit') hidebutton = tag.form(hidebutton, action='#', method='get', class_='inlinebuttons hidefieldchanges') transformer = transformer \ .select('//div[@id="changelog"]/div[@class="change"]/ul[@class="changes"]/li') \ .prepend(hidebutton).end() # return filtered stream return stream | transformer
def render_ticket_action_control(self, req, ticket, action): config = self.parse_config() assert action in config control = [] hints = [] data = config[action] action_name = action # @@TODO: config'able label/name chrome = Chrome(self.env) from trac.ticket.web_ui import TicketModule prepared_fields = TicketModule(self.env)._prepare_fields(req, ticket) for field in data.get('fields', []): id = "action_%s_%s" % (action, field) operation = data.get('operations', {}).get(field, "change") assert operation in ["change", "unset"] if operation == "unset": hints.append("%s will be unset" % field) # @@TODO: i18n continue assert operation == "change" current_value = ticket._old.get(field, ticket[field]) or "" rendered_control = '' prepared_field = [pfield for pfield in prepared_fields if pfield['name'] == field] if len(prepared_field): prepared_field = prepared_field[0] # we can't use chrome.render_template here, or it will blow away # key scripts like jquery.js and trac.js in the eventual 'primary' template # that's rendered by process_request template = chrome.load_template("ticket_fields.html", method="xhtml") rendered_control = template.generate(ticket=ticket, field=prepared_field) if rendered_control: rendered_control = Markup(rendered_control) control.append(tag.label(field, rendered_control or tag.input( name=id, id=id, type='text', value=current_value))) current_status = ticket._old.get('status', ticket['status']) new_status = data['status'].get(current_status) or \ data['status']['*'] if new_status != '*': hints.append("Next status will be %s" % new_status) # @@TODO: i18n add_script(req, "workflow_ticketfields/workflow_ticketfields.js") return (action_name, tag.div(*[tag.div(element, style=("display: inline-block; " "margin-right: 1em")) for element in control], class_="workflow_ticket_fields", style="margin-left: 2em; display: none"), '. '.join(hints) + '.' if hints else '')
def filter_stream(self, req, method, filename, stream, data): if filename == 'timeline.html': filter_projects = self._filtered_projects(req) filter = Transformer('//form[@id="prefs"]/div[1]') stream = stream | filter.before(tag.label("Filter Projects:")) | filter.before(tag.br()) | filter.before(self._projects_field_input(req, filter_projects)) | filter.before(tag.br()) | filter.before(tag.br()) return stream
def prepend_ctxtnav(req, elm_or_label, href): """Prepend an entry to the current page's ctxtnav bar. add_ctxtnav(), sadly, always appends to the right side of the (right-aligned by default) context nav, changing the onscreen locations of the links people are already used to. """ elm = tag(tag.label(elm_or_label, for_='rev'), ' ', tag.input(type='text', id='rev', readonly='readonly', value=href, size=len(href))) req.chrome.setdefault('ctxtnav', []).insert(0, elm)
def _build_renamechildren_field(self): return tag.div(tag.label( tag.input(_("Also rename children"), \ type='checkbox', id='rename_children', \ name='rename_children', checked='checked') \ ), \ class_='field')
def _wiki_edit(self, req, stream): insert = tag.div(class_='field')( tag.label( 'Tag under: (', tag.a('view all tags', href=req.href.tags()), ')', tag.br(), tag.input(id='tags', type='text', name='tags', size='30', value=req.args.get('tags', ' '.join(self._page_tags(req)))), ) ) return stream | Transformer('//div[@id="changeinfo1"]').append(insert)
def _filterBox(self, req, label, name): return tag.label( label, tag.input( type="text", name=name, value=self._session(req, name, ""), style_="width:60%", title=_("available prefixes: contains: ~, starts with: ^, ends with: $"), ), )
def _toggleBox(self, req, label, name, default): if self._session(req, name, default) == 'true': checkbox= tag.input(type= 'checkbox', name= name, value='true', checked='checked') else: checkbox= tag.input(type= 'checkbox', name= name, value= 'true') return checkbox + ' ' + tag.label(label, for_= name)
def _wiki_edit(self, req, stream): # TRANSLATOR: Label text for link to '/tags'. link = tag.a(_("view all tags"), href=req.href.tags()) # TRANSLATOR: ... (view all tags) insert = tag(Markup(_("Tag under: (%(tags_link)s)", tags_link=link))) insert( tag.br(), tag.input(id='tags', type='text', name='tags', size='50', value=req.args.get('tags', ' '.join(self._page_tags(req)))) ) insert = tag.div(tag.label(insert), class_='field') return stream | Transformer('//div[@id="changeinfo1"]').append(insert)
def filter_stream(self, req, method, filename, stream, data): if req.path_info.startswith('/register') and ( req.method == 'GET' or 'registration_error' in data): question = None if self.question is not None and len(self.question)>0: question = tag.label( tag(self.question + " "), tag.input(id='question', type='text', name='question') ) # First Fieldset of the registration form XPath match xpath_match = '//form[@id="acctmgr_registerform"]/fieldset[1]' if question is None: return stream else: return stream | Transformer(xpath_match). \ append(tag(Markup(question))) # Admin Configuration elif req.path_info.startswith('/admin/accounts/config'): api_html = tag.div( tag.label("Question:", for_="registerquestion_question") + tag.input(class_="textwidget", name="question", value=self.question, size=60) ) + tag.div( tag.label("Answer:", for_="registerquestion_answer") + tag.input(class_="textwidget", name="answer", value=self.answer, size=60) ) + tag.div( tag.label("Hint:", for_="registerquestion_hint") + tag.input(class_="textwidget", name="hint", value=self.hint, size=60) ) + tag.br() # First fieldset of the Account Manager config form xpath_match = '//form[@id="accountsconfig"]/fieldset[1]' return stream | Transformer(xpath_match). \ before(tag.fieldset(tag.legend("Anti-Robot Question For Registration") + api_html)) return stream
def _user_field_input(self, req): return tag.div( tag.label( "Include users: ", tag.input( type="text", name="inc_users", value=req.session.get("timeline.filter.inc_users", ""), style_="width:60%", ), ) + tag.br() + tag.label( "Exclude users: ", tag.input( type="text", name="exc_users", value=req.session.get("timeline.filter.exc_users", ""), style_="width:60%", ), ) )
def __new_project(self): all_projects = self.__SmpModel.get_all_projects() return tag.div( tag.label( 'Project:', tag.br(), tag.select( tag.option(), [tag.option(row[1], value=row[0]) for row in sorted(all_projects, key=itemgetter(1))], name="project") ), class_="field")
def _create_select(label_text, id, name, options, selected_name=None, default_selection=None): select = tag.select(id=id, name=name) if selected_name is None and default_selection is not None: selected_name = default_selection for option_name in options: if option_name == selected_name: select.append(tag.option(option_name, value=option_name, selected='selected')) else: select.append(tag.option(option_name, value=option_name)) insert = tag(label_text) insert( tag.br(), select ) insert = tag.div(tag.label(insert), class_='field') return insert
def filter_stream(self, req, method, filename, stream, data): if 'ticket' in data and 'remote_sites' in data: add_script(req, 'tracremoteticket/js/remoteticket.js') transf = Transformer('//select[@id="linked-end"]') label = tag.label(' in ', for_='remote-site') local = tag.option('this project', value=req.href.newticket(), selected='selected') remotes = [tag.option(rs['title'], value=Href(rs['url']).newticket()) for rs in data['remote_sites']] select = tag.select([local] + remotes, id='remote-site') content = label + select stream |= transf.after(content) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == "customfieldadmin.html": add_script(req, "datefield/js/customfield-admin.js") add_stylesheet(req, "datefield/css/customfield-admin.css") stream = stream | Transformer('.//select[@id="type"]').append( tag.option("Date", value="date", id="date_type_option") ) stream = stream | Transformer('.//form[@id="addcf"]/fieldset/div[@class="buttons"]').before( tag.div( tag.input(id="date_empty", type="checkbox", name="date_empty"), tag.label("Allow empty date"), for_="date_empty", class_="field", id="date_empty_option", ) ) return stream
def __edit_project(self, data): component = data.get('component').name all_projects = self.__SmpModel.get_all_projects() id_project_component = self.__SmpModel.get_id_projects_component(component) id_projects_selected = [] for id_project in id_project_component: id_projects_selected.append(id_project[0]) return tag.div( tag.label( 'Available in Project(s):', tag.br(), tag.select( tag.option("All", value="0"), [tag.option(row[1], selected=(row[0] in id_projects_selected or None), value=row[0]) for row in sorted(all_projects, key=itemgetter(1))], name="project", multiple="multiple", size="10") ), class_="field")
def __edit_project(self, data): milestone = data.get('milestone').name all_projects = self.__SmpModel.get_all_projects() id_project_milestone = self.__SmpModel.get_id_project_milestone(milestone) if id_project_milestone != None: id_project_selected = id_project_milestone[0] else: id_project_selected = None return tag.div( tag.label( 'Project:', tag.br(), tag.select( tag.option(), [tag.option(row[1], selected=(id_project_selected == row[0] or None), value=row[0]) for row in sorted(all_projects, key=itemgetter(1))], name="project") ), class_="field")
def _version_edit(self, data): milestone = data.get('milestone').name db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT version FROM milestone_version WHERE milestone=%s", (milestone,)) row = cursor.fetchone() value = row and row[0] cursor.execute("SELECT name FROM version WHERE time IS NULL OR time = 0 OR time>%s " "OR name = %s ORDER BY name", (to_timestamp(None), value)) return tag.div( tag.label( 'Version:', tag.br(), tag.select( tag.option(), [tag.option(row[0], selected=(value == row[0] or None)) for row in cursor], name="version")), class_="field")
def _version_edit(self, data): milestone = data.get('milestone').name db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "SELECT version FROM milestone_version WHERE milestone=%s", (milestone, )) row = cursor.fetchone() value = row and row[0] cursor.execute( "SELECT name FROM version WHERE time IS NULL OR time = 0 OR time>%s " "OR name = %s ORDER BY name", (to_timestamp(None), value)) return tag.div(tag.label( 'Version:', tag.br(), tag.select(tag.option(), [ tag.option(row[0], selected=(value == row[0] or None)) for row in cursor ], name="version")), class_="field")
def get_content(self, req): """Returns the Genshi tags for the new HTML elements representing the Captcha. """ values = {} values['ip'] = req.remote_addr values['submitted'] = int(time.time()) math_problem_text = self.create_math_problem(values) values['author'] = req.args.get('author') values['summary'] = req.args.get('field_summary') values['text'] = self.get_failed_attempt_text(req) values['href'] = req.path_info # Save the problem so that the post request of the web server knows # which request to process. This is required on FCGI and mod_python # web servers, because there may be many different processes handling # the request and there's no guarantee that the same web server will # handle both the form display and the form submit. db = self.env.get_db_cnx() cursor = db.cursor() fields = values.keys() cursor.execute("INSERT INTO mathcaptcha_history (%s) VALUES (%s)" % (','.join(fields), ','.join(['%s'] * len(fields))), [values[name] for name in fields]) id = db.get_last_id(cursor, 'mathcaptcha_history') db.commit() self.env.log.debug("%s %s %s%s: generating math solution: id=%d, %d %s %d = %d" % (req.remote_addr, req.remote_user, req.base_path, req.path_info, id, values['left_operand'], values['operator'], values['right_operand'], values['solution'])) # Obfuscating the names of the input variables to trick spam harvesters # to put other data in the solutions field. The math solution is # named "email" in the hopes that it will attract email addresses # instead of numbers. The database key is named "url" to try to # attract non-numbers content = tag.div()( tag.label('Anonymous users are allowed to post by %s ' % math_problem_text) + tag.input(type='text', name='email', class_='textwidget', size='5') + tag.input(type='hidden', name='url', value=str(id + self.id_offset)) ) return content
def filter_stream(self, req, method, filename, stream, data): """Return a filtered Genshi event stream, or the original unfiltered stream if no match. `req` is the current request object, `method` is the Genshi render method (xml, xhtml or text), `filename` is the filename of the template to be rendered, `stream` is the event stream and `data` is the data for the current template. See the Genshi documentation for more information. """ #self.env.log.info(filename) add_captcha = False if data['authname'] == 'anonymous': if self.is_banned(req): self.env.log.debug("%s %s %s%s: IP banned as spammer" % (req.remote_addr, req.remote_user, req.base_path, req.path_info)) stream = tag.label("System offline.") return stream href = req.path_info self.env.log.debug(href) self.env.log.debug(filename) if filename == "ticket.html": if "newticket" in href: add_captcha = 'TICKET_CREATE' in req.perm elif "ticket" in href: add_captcha = 'TICKET_MODIFY' in req.perm or 'TICKET_APPEND' in req.perm elif filename == "wiki_edit.html": add_captcha = 'WIKI_MODIFY' in req.perm if add_captcha: # Insert the math question right before the submit buttons stream = stream | Transformer('//div[@class="buttons"]').before( self.get_content(req)) return stream
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 = is_enabled(self.env, EmailVerificationModule) and \ AccountManager(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 filter_stream(self, req, method, filename, stream, data): if filename == "customfieldadmin.html": add_script(req, 'datefield/js/customfield-admin.js') add_stylesheet(req, 'datefield/css/customfield-admin.css') stream = stream | Transformer('.//select[@id="type"]').append( tag.option('Date', value='date', id="date_type_option") ) stream = stream | Transformer( './/form[@id="addcf"]/fieldset/div[@class="buttons"]' ).before( tag.div( tag.input( id="date_empty", type="checkbox", name="date_empty" ), tag.label('Allow empty date'), for_="date_empty", class_="field", id="date_empty_option" ) ) return stream
def _contruct_tickets_table(self, req, ticket, childtickets, priorities): # trac.ini : Which columns to display in child ticket listing? columns = self.config.getlist('childtickets', 'parent.%s.table_headers' % ticket['type'], default=['summary','owner']) tablediv = tag.div() tablediv.append(tag.label(tag.input(type="checkbox", id="cb_show_closed"), "show closed tickets")) for tkt_types, type_tickets in groupby(childtickets, lambda t: t['type']): tablediv.append(tag.h2("Type: %s" % tkt_types, class_="report-result")) tablediv.append( tag.table( tag.thead( tag.tr( tag.th("Ticket",class_="id"), [ tag.th(s.title(),class_=s) for s in columns ]) ), tag.tbody([ self._table_row(req,tkt,columns,priorities) for tkt in type_tickets]), class_="listing tickets", ) ) return tablediv
def _toggleBox(self, req, label, name, default): if self._session(req, name, default) == "true": checkbox = tag.input(type="checkbox", name=name, value="true", checked="checked") else: checkbox = tag.input(type="checkbox", name=name, value="true") return checkbox + " " + tag.label(label, for_=name)
def _filterBox(self, req, label, name): return tag.label(label, tag.input(type= "text", name= name, value= self._session(req, name, ''), style_= "width:60%"))
def filter_stream(self, req, method, filename, stream, data): if filename not in ('ticket.html', 'ticket.rss'): return stream ticket_id = req.args.get('id') if not ticket_id: return stream # determine the username of the current user user = req.authname # determine if the user has the permission to see private comments perms = PermissionSystem(self.env) has_private_permission = \ self.private_comment_permission in perms.get_user_permissions(user) # Remove private comments from Ticket Page if filename == 'ticket.html': buf = StreamBuffer() def check_comments(): delimiter = '<div xmlns="http://www.w3.org/1999/xhtml" ' + \ 'class="change" id="trac-change-' comment_stream = str(buf) # split the comment_stream to get single comments comments_raw = comment_stream.split(delimiter) comment_stream = '' for comment in comments_raw: if comment is None or len(comment) < 1: continue # determine comment id find = comment.find('">') if find == -1: continue comment_id = comment[:find] # concat the delimiter and the comment again comment_code = delimiter + comment # if the user has the permission to see the comment # the comment_code will be appended to the comment_stream comment_private = self._is_comment_private( ticket_id, comment_id) if comment_private: comment_code = comment_code.replace( '<span class="threading">', '<span class="threading"> <span class="%s">' 'this comment is private</span>' % str(self.css_class_private_comment_marker)) if has_private_permission or not comment_private: comment_stream += comment_code return HTML(comment_stream) # filter all comments stream |= Transformer('//div[@class="change" and @id]') \ .copy(buf).replace(check_comments) # if the user has the private comment permission the checkboxes # to change the private value will be added if has_private_permission: comment_box = tag.label( _("Private Comment:"), tag.input(type='checkbox', name='private_comment')) stream |= Transformer('//h2[@id="trac-add-comment"]') \ .after(comment_box) # Trac 1.0 and later: # stream |= Transformer( # '//div[@id="trac-add-comment"]//fieldset').prepend(input) # Remove private comments from ticket RSS feed if filename == 'ticket.rss': comments = self._get_all_private_comments(ticket_id) self.log.debug("Private Comments for Ticket %d: %s" % (ticket_id, comments)) for comment_id in comments: stream |= Transformer('//item[%d]' % comment_id).remove() return stream
def filter_stream(self, req, method, filename, stream, data): if filename == "ticket.html": ##Check Permissions enchants = self.config.get('blackmagic', 'tweaks', '') for field in (x.strip() for x in enchants.split(',')): self.env.log.debug("Checking %s:" % field) disabled = False hidden = False #Get a list of the permissions from the config, split them on commas and remove spaces perms = self.config.get('blackmagic', '%s.permission' % field, '').upper() #Default to not having permissions hasPerm = True if perms: hasPerm = False #If there are permissions self.env.log.debug("perm: %s" % len(perms)) perms = perms.split(',') #walk the list we got back and check the request for the permission for perm in perms: perm = perm.strip() self.env.log.debug("Checking perm: %s" % perm) if perm in req.perm: self.env.log.debug("User has perm: %s" % perm) hasPerm = True if hasPerm == False: denial = self.config.get('blackmagic', '%s.ondenial' % field, None) if denial: if denial == "disable": disabled = True elif denial == "hide": hidden = True else: disabled = True else: disabled = True self.env.log.debug('hasPerm: %s' % hasPerm) if hidden == False: if self.config.get('blackmagic', '%s.label' % field, None): labelTXT = self.config.get('blackmagic', '%s.label' % field) label = tag.label("%s:" %labelTXT, for_="field-%s" % field) stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(label) if hasPerm == False: if istrue(self.config.get('blackmagic', '%s.hide' % field, None)): hidden = True if disabled or istrue(self.config.get('blackmagic', '%s.disable' % field, False)): stream = stream | Transformer('//*[@id="field-%s"]' % field).attr("disabled", "disabled") label = self.config.get('blackmagic', '%s.label' % field) if not label: label = field.capitalize() if not self.gray_disabled: stream = stream | Transformer('//label[@for="field-%s"]' % field).replace( tag.strike()('%s:' % label) ) else: stream = stream | Transformer('//label[@for="field-%s"]' % field).replace( tag.span(style="color:%s" % self.gray_disabled)('%s:' % label) ) if hidden or istrue(self.config.get('blackmagic', '%s.hide' % field, None)): stream = stream | Transformer('//th[@id="h_%s"]' % field).replace(" ") stream = stream | Transformer('//td[@headers="h_%s"]' % field).replace(" ") stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(" ") stream = stream | Transformer('//*[@id="field-%s"]' % field).replace(" ") if hidden == False: if self.config.get('blackmagic', '%s.notice' % field, None): stream = stream | Transformer('//*[@id="field-%s"]' % field).after( tag.br() + tag.small(class_="notice-%s" %field)( tag.em()( Markup(self.config.get('blackmagic', '%s.notice' % field)) ) ) ) tip = self.config.get('blackmagic', '%s.tip' % field, None) if tip: stream = stream | Transformer('//div[@id="banner"]').before( tag.script(type="text/javascript", src=req.href.chrome("blackmagic", "js", "wz_tooltip.js"))() ) stream = stream | Transformer('//*[@id="field-%s"]' % field).attr( "onmouseover", "Tip('%s')" % tip.replace(r"'", r"\'") ) return stream
def filter_stream(self, req, method, filename, stream, data): field_name = self._get_field_name(req.args) if 'TICKET_ADMIN' in req.perm and field_name and \ req.path_info.startswith('/admin/ticket'): if field_name in self._non_abstract_enums: field_objects = data.get(field_name + 's') else: field_objects = data.get('enums') default_ccs = DefaultCC.select(self.env, field_name) if field_objects: # list of field objects stream = stream | Transformer( '//table[@class="listing"]/thead/tr').append( tag.th('CC')) if field_name == 'component': transformer = Transformer('//table[@id="complist"]/tbody') default_comp = self.config.get( 'ticket', 'default_component') for idx, field_object in enumerate(field_objects): # Milestone object can be a tuple :/ try: field_object_name = field_object.name except AttributeError: field_object_name = field_object[0].name if field_object_name in default_ccs: default_cc = default_ccs[field_object_name] else: default_cc = '' # Annoyingly, we can't just append to the row if the # collection is components, it appears to blat it for # rendering later so you end up with no rows # This may be due to the collection being a generator, # but then again, who knows? if field_name == 'component': if default_comp == field_object_name: default_tag = tag.input( type='radio', name='default', value=field_object_name, checked='checked') else: default_tag = tag.input( type='radio', name='default', value=field_object_name) transformer = transformer.append( tag.tr( tag.td( tag.input(type='checkbox', name='sel', value=field_object_name), class_='sel'), tag.td( tag.a(field_object_name, href=req.href.admin( 'ticket', 'components') + '/' + \ field_object_name), class_='name'), tag.td(field_object.owner, class_='owner'), tag.td(default_tag, class_='default'), tag.td(default_cc, class_='defaultcc') ) ) else: stream = stream | Transformer( '//table[@class="listing"]/tbody/tr[%s]' % (idx+1,) ).append(tag.td(default_cc, class_='defaultcc')) if field_name == 'component': return stream | transformer else: # edit field object if field_name in self._non_abstract_enums: field_object = data.get(field_name) else: field_object = data.get('enum') if field_object: # Milestone object can be a tuple :/ try: field_object_name = field_object.name except AttributeError: field_object_name = field_object[0] if field_object_name in default_ccs: default_cc = default_ccs[field_object_name] else: default_cc = '' transformer = Transformer( '//form[@class="mod"]/fieldset/div[@class="buttons"]') transformer = transformer.before( tag.div(tag.label("Default CC:"), tag.br(), tag.input(type="text", name="defaultcc", value=default_cc), class_="field") ).before(tag.input(type='hidden', name='old_name', value=field_object_name)) return stream | transformer return stream
class AddCommentMacro(WikiMacroBase): """A macro to add comments to a page. Usage: {{{ [[AddComment]] }}} The macro accepts one optional argument that allows appending to the wiki page even though user may not have modify permission: {{{ [[AddComment(appendonly)]] }}} """ implements(IWikiMacroProvider, IRequestFilter, IMacroPoster) def expand_macro(self, formatter, name, content): args, kw = parse_args(content) req = formatter.req context = formatter.context # Prevent multiple inclusions - store a temp in req if hasattr(req, 'addcommentmacro'): raise TracError('\'AddComment\' macro cannot be included twice.') req.addcommentmacro = True # Prevent it being used outside of wiki page context resource = context.resource if not resource.realm == 'wiki': raise TracError( '\'AddComment\' macro can only be used in Wiki pages.') # Setup info and defaults authname = req.authname page = WikiPage(self.env, resource) page_url = req.href.wiki(resource.id) wikipreview = req.args.get("preview", "") # Can this user add a comment to this page? appendonly = ('appendonly' in args) cancomment = False if page.readonly: if 'WIKI_ADMIN' in req.perm(resource): cancomment = True elif 'WIKI_MODIFY' in req.perm(resource): cancomment = True elif appendonly and 'WIKI_VIEW' in req.perm(resource): cancomment = True else: self.log.debug( 'Insufficient privileges for %s to AddComment to %s', req.authname, resource.id) # Get the data from the POST comment = req.args.get("addcomment", "") preview = req.args.get("previewaddcomment", "") cancel = req.args.get("canceladdcomment", "") submit = req.args.get("submitaddcomment", "") if not cancel and req.authname == 'anonymous': authname = req.args.get("authoraddcomment", authname) # Ensure [[AddComment]] is not present in comment, so that infinite # recursion does not occur. comment = to_unicode( re.sub('(^|[^!])(\[\[AddComment)', '\\1!\\2', comment)) the_preview = the_message = the_form = tag() # If we are submitting or previewing, inject comment as it should look if cancomment and comment and (preview or submit): heading = tag.h4("Comment by ", authname, " on ", to_unicode(time.strftime('%c', time.localtime())), id="commentpreview") if preview: the_preview = tag.div(heading, format_to_html(self.env, context, comment), class_="wikipage", id="preview") # Check the form_token form_ok = True if submit and req.args.get('__FORM_TOKEN', '') != req.form_token: form_ok = False the_message = tag.div(tag.strong("ERROR: "), "AddComment received incorrect form token. " "Do you have cookies enabled?", class_="system-message") # When submitting, inject comment before macro if comment and submit and cancomment and form_ok: submitted = False newtext = "" for line in page.text.splitlines(): if line.find('[[AddComment') == 0: newtext += "==== Comment by %s on %s ====\n%s\n\n" % ( authname, to_unicode(time.strftime('%c', time.localtime())), comment) submitted = True newtext += line + "\n" if submitted: page.text = newtext # Let the wiki page manipulators have a look at the # submission. valid = True req.args.setdefault('comment', 'Comment added.') try: for manipulator in WikiModule(self.env).page_manipulators: for field, message in manipulator.validate_wiki_page( req, page): valid = False if field: the_message += tag.div(tag.strong( "invalid field '%s': " % field), message, class_="system-message") else: the_message += tag.div(tag.strong("invalid: "), message, class_="system-message") # The TracSpamfilterPlugin does not generate messages, # but throws RejectContent. except TracError, s: valid = False the_message += tag.div(tag.strong("ERROR: "), s, class_="system-message") if valid: page.save(authname, req.args['comment'], req.environ['REMOTE_ADDR']) # We can't redirect from macro as it will raise RequestDone # which like other macro errors gets swallowed in the Formatter. # We need to re-raise it in a post_process_request instead. try: self.env.log.debug( "AddComment saved - redirecting to: %s" % page_url) req._outheaders = [] req.redirect(page_url) except RequestDone: req.addcomment_raise = True else: the_message = tag.div( tag.strong("ERROR: "), "[[AddComment]] " "macro call must be the only content on its line. " "Could not add comment.", class_="system-message") the_form = tag.form( tag.fieldset( tag.legend("Add comment"), tag.div((wikipreview and "Page preview..." or None), tag.textarea((not cancel and comment or ""), class_="wikitext", id="addcomment", name="addcomment", cols=80, rows=5, disabled=(not cancomment and "disabled" or None)), class_="field"), (req.authname == 'anonymous' and tag.div( tag.label("Your email or username:"******"authoraddcomment"), tag.input(id="authoraddcomment", type="text", size=30, value=authname, name="authoraddcomment", disabled=(not cancomment and "disabled" or None))) or None), tag.input(type="hidden", name="__FORM_TOKEN", value=req.form_token), tag.div(tag.input(value="Add comment", type="submit", name="submitaddcomment", size=30, disabled=(not cancomment and "disabled" or None)), tag.input(value="Preview comment", type="submit", name="previewaddcomment", disabled=(not cancomment and "disabled" or None)), tag.input(value="Cancel", type="submit", name="canceladdcomment", disabled=(not cancomment and "disabled" or None)), class_="buttons"), ), method="post", action=page_url + "#commenting", ) if not wikipreview: # Wiki edit preview already adds this javascript file add_script(req, 'common/js/wikitoolbar.js') return tag.div(the_preview, the_message, the_form, id="commenting")
def AddComment(macro, environ, data, *args, **kwargs): """Display an add comment form allowing users to post comments. This macro allows you to display an add comment form on the current page allowing users to post comments. The comments are added to the page's content itself. **Arguments:** //No Arguments// **Example(s):** {{{ <<AddComment>> }}} <<AddComment>> """ # Setup info and defaults parser = environ.parser request = environ.request page = data["page"] page_name = page["name"] page_text = page["text"] # Get the data from the POST comment = request.kwargs.get("comment", "") action = request.kwargs.get("action", "") author = request.kwargs.get("author", environ._user()) # Ensure <<AddComment>> is not present in comment, so that infinite # recursion does not occur. comment = re.sub("(^|[^!])(\<\<AddComment)", "\\1!\\2", comment) the_preview = None the_comment = None # If we are submitting or previewing, inject comment as it should look if action == "preview": the_preview = tag.div(tag.h1("Preview"), id="preview") the_preview += tag.div(parser.generate(comment, environ=(environ, data)), class_="article") # When submitting, inject comment before macro if comment and action == "save": new_text = "" comment_text = "\n==== Comment by %s on %s ====\n\n%s\n\n" % ( author, time.strftime('%c', time.localtime()), comment) for line in page_text.split("\n"): if line.find("<<AddComment") == 0: new_text += comment_text new_text += line + "\n" search = environ.search storage = environ.storage storage.reopen() search.update(environ) storage.save_text(page_name, new_text, author, "Comment added by %s" % author) search.update_page(environ.get_page(page_name), page_name, text=new_text) the_comment = tag.div(parser.generate(comment_text, environ=(environ, data)), class_="article") the_form = tag.form( tag.input(type="hidden", name="parent", value=page["node"]), tag.fieldset( tag.legend("Add Comment"), tag.p(tag.textarea( (not action in ("cancel", "save") and comment or ""), id="comment", name="comment", cols=80, rows=5), class_="text"), tag.h4(tag.label("Your email or username:"******"author")), tag.p(tag.input(id="author", name="author", type="text", value=(not action in ("cancel", "save")) and author or ""), class_="input"), tag.p(tag.button("Preview", type="submit", name="action", value="preview"), tag.button("Save", type="submit", name="action", value="save"), tag.button("Cancel", type="submit", name="action", value="cancel"), class_="button"), ), method="post", action="") return tag(the_preview, the_comment, the_form)
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.') # 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="accountsconfig"]/fieldset[1]' return stream | Transformer(xpath_match). \ before(tag.fieldset(tag.legend("reCAPTCHA") + api_html + tag.br() + theme_html + language_html)) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == 'timeline.html': # Insert the new field for entering user names filter = Transformer('//form[@id="prefs"]/fieldset') return stream | filter.before(tag.br()) | filter.before(tag.label("Filter Components (none for all): ")) | filter.before(tag.br()) | filter.before(self._components_field_input(req)) return stream