Пример #1
0
    def process_request(self, req):
        if req.is_xhr and req.method == 'POST' and 'save_prefs' in req.args:
            self._do_save_xhr(req)

        panels, providers = self._get_panels(req)
        if not panels:
            raise HTTPNotFound(_("No preference panels available"))

        panels = []
        child_panels = {}
        providers = {}
        for provider in self.panel_providers:
            for panel in provider.get_preference_panels(req) or []:
                if len(panel) == 3:
                    name, label, parent = panel
                    child_panels.setdefault(parent, []).append((name, label))
                else:
                    name = panel[0]
                    panels.append(panel)
                providers[name] = provider
        panels = sorted(panels)

        panel_id = req.args.get('panel_id')
        if panel_id is None:
            panel_id = panels[1][0] \
                       if len(panels) > 1 and panels[0][0] == 'advanced' \
                       else panels[0][0]
        chosen_provider = providers.get(panel_id)
        if not chosen_provider:
            raise HTTPNotFound(_("Unknown preference panel '%(panel)s'",
                                 panel=panel_id))

        session_data = {
            'session': req.session,
            'settings': {'session': req.session,  # Compat: remove in 1.3.1
                         'session_id': req.session.sid},
        }

        # Render child preference panels.
        chrome = Chrome(self.env)
        children = []
        if child_panels.get(panel_id):
            for name, label in child_panels[panel_id]:
                ctemplate, cdata = provider.render_preference_panel(req, name)
                cdata.update(session_data)
                rendered = chrome.render_template(req, ctemplate, cdata,
                                                  fragment=True)
                children.append((name, label, rendered))

        template, data = \
            chosen_provider.render_preference_panel(req, panel_id)
        data.update(session_data)
        data.update({
            'active_panel': panel_id,
            'panels': panels,
            'children': children,
        })

        add_stylesheet(req, 'common/css/prefs.css')
        return template, data, None
Пример #2
0
    def _do_deploy(self, dest):
        target = os.path.normpath(dest)
        chrome_target = os.path.join(target, "htdocs")
        script_target = os.path.join(target, "cgi-bin")

        # Copy static content
        makedirs(target, overwrite=True)
        makedirs(chrome_target, overwrite=True)
        from trac.web.chrome import Chrome

        printout(_("Copying resources from:"))
        for provider in Chrome(self.env).template_providers:
            paths = list(provider.get_htdocs_dirs() or [])
            if not len(paths):
                continue
            printout("  %s.%s" % (provider.__module__, provider.__class__.__name__))
            for key, root in paths:
                if not root:
                    continue
                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
        makedirs(script_target, overwrite=True)
        printout(_("Creating scripts."))
        data = {"env": self.env, "executable": sys.executable}
        for script in ("cgi", "fcgi", "wsgi"):
            dest = os.path.join(script_target, "trac." + script)
            template = Chrome(self.env).load_template("deploy_trac." + script, "text")
            stream = template.generate(**data)
            with open(dest, "w") as out:
                stream.render("text", out=out, encoding="utf-8")
Пример #3
0
 def filter_stream(self, req, method, filename, stream, data):
     if req.get_header("X-Moz") == "prefetch":
         return stream
     if filename == "ticket.html":
         if not self.check_permissions(req):
             return stream
         chrome = Chrome(self.env)
         filter = Transformer('//fieldset[@id="properties"]')
         # add a hidden div to hold the ticket_fields input
         snippet = tag.div(style="display:none;")
         snippet = tag.input(type="hidden", id="field-ticket_fields", name="field_ticket_fields", value=','.join(data['ticket_fields']))
         stream = stream | filter.after(snippet)
         if req.path_info != '/newticket':
             # insert the ticket field groups after the standard trac 'Change Properties' field group
             stream = stream | filter.after(chrome.render_template(req, 'ticket_fields_datatable.html', data, fragment=True))
     elif filename == "admin_enums.html":
         if not self.check_permissions(req) or not req.args.get('path_info'):
             return stream
         for k,v in {'cat_id':'ticket', 'panel_id':'type'}.iteritems():
             if k not in req.args or req.args.get(k) != v:
                 return stream
         if 'ticket_fields' in data:
             chrome = Chrome(self.env)
             filter = Transformer('//div[@class="buttons"]')
             # add a hidden div to hold the ticket_fields input
             snippet = tag.div(style="display:none;")
             snippet = tag.input(type="hidden", id="field-ticket_fields", name="field_ticket_fields", value=','.join(data['ticket_fields']))
             stream = stream | filter.before(snippet)
             stream = stream | filter.before(chrome.render_template(req, 'ticket_fields_datatable.html', data, fragment=True))
     return stream
Пример #4
0
    def expand_macro(self, formatter, name, content, args=None):
        """
        Returns the outcome from macro.
        """
        req = formatter.req
        userstore = get_userstore()
        user = userstore.getUser(req.authname)
        msgsrv = self.env[MessageService]

        # Parse optional arguments
        if args is None:
            args = parse_args(content)
            if len(args) > 1:
                args = args[1]

        data = {
            'groups': msgsrv.get_messages_grouped_by(user.id)
        }

        # FIXME: Temporary fix for IE8 + jQuery 1.4.4 + Transparency combination
        agent = req.get_header('user-agent')
        if agent and 'MSIE 8.0' not in agent:
            add_script(req, 'multiproject/js/transparency.js')

        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/messages_group_macro.js')

        chrome = Chrome(self.env)
        return chrome.render_template(req, 'multiproject_messages_group_macro.html', data, fragment=True)
Пример #5
0
def get_diffs(self, req, title, old_text, new_text):
    diff_style, diff_options, diff_data = get_diff_options(req)
    diff_context = 3
    for option in diff_options:
        if option.startswith('-U'):
            diff_context = int(option[2:])
            break
    if diff_context < 0:
        diff_context = None
    diffs = diff_blocks(old_text.splitlines(), new_text.splitlines(), context=diff_context,
                        tabwidth=2,
                        ignore_blank_lines=True,
                        ignore_case=True,
                        ignore_space_changes=True)
    
    chrome = Chrome(self.env)
    loader = TemplateLoader(chrome.get_all_templates_dirs())
    tmpl = loader.load('diff_div.html')
    
    changes=[{'diffs': diffs, 'props': [],
              'title': title,
              'new': {'path':"", 'rev':'', 'shortrev': '', 'href':''},
              'old': {'path':"", 'rev':'', 'shortrev': '', 'href': ''}}]

    data = chrome.populate_data(req,
                                { 'changes':changes , 'no_id':True, 'diff':diff_data,
                                  'longcol': '', 'shortcol': ''})
    diff_data['style']='sidebyside';
    data.update({ 'changes':changes , 'no_id':True, 'diff':diff_data,
                  'longcol': '', 'shortcol': ''})
    stream = tmpl.generate(**data)
    return stream.render()
Пример #6
0
    def test_no_reports(self):
        req = MockRequest()
        config = Mock(name='trunk', min_rev_time=lambda env: 0,
                      max_rev_time=lambda env: 1000, path='tmp/')
        build = Build(self.env, config='trunk', platform=1, rev=123,
                      rev_time=42)
        build.insert()
        step = BuildStep(self.env, build=build.id, name='foo',
                         status=BuildStep.SUCCESS)
        step.insert()

        summarizer = PyLintSummarizer(self.env)
        template, data = summarizer.render_summary(req, config, build, step,
                                                   'lint')
        self.assertEqual('bitten_summary_lint.html', template)
        self.assertEqual([], data['data'])
        self.assertEqual({'category': {'convention': 0, 'refactor': 0,
                                       'warning': 0, 'error': 0},
                          'files': 0, 'lines': 0, 'type': {}}, data['totals'])

        stream = Chrome(self.env).render_template(req, template,
                                                  {'data': data}, 'text/html',
                                                  fragment=True)
        stream = Stream(list(stream))
        for i, category in enumerate(("Convention", "Refactor", "Warning",
                                      "Error", "Totals")):
            text = stream.select('//tbody[@class="totals"]//td[%d]/text()'
                                 % (i + 1)).render()
            self.assertEqual('0', text, "Expected total for %r to have "
                             "value '0' but got %r" % (category, text))
Пример #7
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req

        # Parse arguments
        args, kwargs = parse_args(content, strict=False)
        assert not args and not ('status' in kwargs or 'format' in kwargs), \
          "Invalid input!"
        # hack the `format` kwarg in order to display all-tickets stats
        # when no kwargs are supplied
        kwargs['format'] = 'count'

        # special case for values equal to 'self': replace with current
        # ticket number, if available
        for key in kwargs.keys():
            if kwargs[key] == 'self':
                current_ticket = self._this_ticket(req)
                if current_ticket: kwargs[key] = current_ticket

        # Create & execute the query string
        qstr = '&'.join(['%s=%s' % item
                                for item in kwargs.iteritems()])
        query = Query.from_string(self.env, qstr, max=0)

        # Calculate stats
        qres = query.execute(req)
        tickets = apply_ticket_permissions(self.env, req, qres)

        stats = get_ticket_stats(self.stats_provider, tickets)
        stats_data = query_stats_data(req, stats, query.constraints)

        # ... and finally display them
        add_stylesheet(req, 'common/css/roadmap.css')
        chrome = Chrome(self.env)
        return chrome.render_template(req, 'progressmeter.html', stats_data,
                                      fragment=True)
    def _generate_form(self, req, data):
        userQueryData = dict(data)

        userQueryData['user_queries']   =[]

        userQueryData['empty_url']      = req.href.report()

        self.log.debug("Generating UserQueries navigation selector")

        # not really sure which is the best/definitive way yet:
        if userQueryData['report']:
            # SQL queries
            userQueryData['current_report_id'] = userQueryData['report']['id']
        elif userQueryData.has_key('report_resource'):
            # Custom queries in 0.11?
            userQueryData['current_report_id'] = int(userQueryData['report_resource'].id)
        elif userQueryData.has_key('context') and userQueryData['context'].req.args.has_key('report'):
            # Custom queries in 0.12?            
            userQueryData['current_report_id'] = int(userQueryData['context'].req.args['report'])
        else:
            # User visiting /report
            userQueryData['current_report_id'] = -1

        self.log.debug("User is visiting report %d", userQueryData['current_report_id'])

        db = self.env.get_db_cnx()
        cursor = db.cursor()
        cursor.execute("SELECT id AS report, title FROM report ORDER BY report")
        userQueryData['project_queries'] = [{'url': req.href.report(id),
                                             'id': id, 'name': title}
                                            for id, title in cursor]

        stream = Chrome(self.env).render_template(req, 'userqueries.html',
              userQueryData, fragment=True)
        return stream.select('//form')
Пример #9
0
    def _create_page_param(self, req, page_param):
        # page_param['workflow_config']
        
        # sort config for display
        section = self.config['ticket-workflow']
        name_list = []
        for (name, value) in section.options():
            name_list.append(name)
        name_list.sort()

        # create config data for display
        ret_val = ''
        for name in name_list:
            ret_val += name + '=' + section.get(name) + '\n'
        
        page_param['workflow_config'] = ret_val
        
        # page_param['workflow_default_config']
        
        # localization
        locale = LocaleUtil().get_locale(req)
        if (locale == 'ja'):
            init_file = 'trac_jp.ini'
        else:
            init_file = 'trac.ini'
        
        # read defalut config
        template = Chrome(self.env).load_template(init_file, 'text')
        stream = template.generate()
        default_config = stream.render('text', encoding=None)
        
        page_param['workflow_default_config'] = default_config
Пример #10
0
 def expand_macro(self, formatter, name, args):
     req = formatter.req
     chrome = Chrome(self.env)
     report = ReportModule(self.env)
     
     comma_splitter = re.compile(r'(?<!\\),')
     kwargs = {}
     for arg in comma_splitter.split(args):
         arg = arg.replace(r'\,', ',')
         m = re.match(r'\s*[^=]+=', arg)
         if m:
             kw = arg[:m.end() - 1].strip()
             value = arg[m.end():]
             if re.match(r'^\$[A-Z]*$', value):
                value = req.args.get(value[1:])
             kwargs[kw] = value if value!= None else ''
         else:
             if re.match(r'^\$[A-Z]*$', arg):
                arg = req.args.get(arg[1:])
             id = int(arg)
     
     req.args = kwargs
     req.args['page'] = '1'
     template, data, content_type = report._render_view(req, id)
     add_stylesheet(req, 'common/css/report.css')
     
     fullpath = ''
     if pkg_resources.resource_exists('wikireport', 'WikiReport.html'):
         fullpath = pkg_resources.resource_filename('wikireport', 'WikiReport.html')
     else:
         filepath = os.path.dirname(os.path.abspath( __file__ ))
         fullpath = os.path.join(filepath, 'WikiReport.html')
 
     return chrome.render_template(req, fullpath, data, None, fragment=True)
Пример #11
0
    def _render_editor(self, req, milestone):
        # Suggest a default due time of 18:00 in the user's timezone
        now = datetime.now(req.tz)
        default_due = datetime(now.year, now.month, now.day, 18)
        if now.hour > 18:
            default_due += timedelta(days=1)
        default_due = to_datetime(default_due, req.tz)

        data = {
            'milestone': milestone,
            'datetime_hint': get_datetime_format_hint(req.lc_time),
            'default_due': default_due,
            'milestone_groups': [],
        }

        if milestone.exists:
            req.perm(milestone.resource).require('MILESTONE_MODIFY')
            milestones = [m for m in Milestone.select(self.env)
                          if m.name != milestone.name
                          and 'MILESTONE_VIEW' in req.perm(m.resource)]
            data['milestone_groups'] = group_milestones(milestones,
                'TICKET_ADMIN' in req.perm)
        else:
            req.perm(milestone.resource).require('MILESTONE_CREATE')

        chrome = Chrome(self.env)
        chrome.add_jquery_ui(req)
        chrome.add_wiki_toolbars(req)
        return 'milestone_edit.html', data, None
Пример #12
0
    def expand_macro(self, formatter, name, content, args=None):
        """
        Returns the outcome from macro.
        """
        req = formatter.context.req

        # Check permissions
        if 'TIMELINE_VIEW' not in req.perm:
            # Return default content / instructions
            return tag.div(
                tag.h2(_('Project team'), **{'class': 'title'}),
                tag.p(_('Project team cannot be found or no permission to follow it')),
                **{'class': 'watch'}
            )

        # Load project info from optional project argument. Defaults to current env
        project = Project.get(self.env)
        team, members = self._get_team_info(project)

        # Return rendered HTML with JS attached to it
        data = {
            'project_id': project.id,
            'env_name': self.env.project_identifier,
            'project_name': self.env.project_identifier,  # TODO: redundant
            'team': team,
            'members': members
        }

        # NOTE: Use fragment to not to recreate chrome (add_script does not work) and run post processing manually
        chrome = Chrome(self.env)
        stream = chrome.render_template(req, 'multiproject_team.html', data, fragment=True)
        if req.form_token:
            stream |= chrome._add_form_token(req.form_token)

        return stream
Пример #13
0
    def test_icon_links(self):
        req = Request(abs_href=Href('http://example.org/trac.cgi'),
                      href=Href('/trac.cgi'), base_path='/trac.cgi',
                      path_info='',
                      add_redirect_listener=lambda listener: None)
        chrome = Chrome(self.env)

        # No icon set in config, so no icon links
        self.env.config.set('project', 'icon', '')
        links = chrome.prepare_request(req)['links']
        assert 'icon' not in links
        assert 'shortcut icon' not in links

        # Relative URL for icon config option
        self.env.config.set('project', 'icon', 'foo.ico')
        links = chrome.prepare_request(req)['links']
        self.assertEqual('/trac.cgi/chrome/common/foo.ico',
                         links['icon'][0]['href'])
        self.assertEqual('/trac.cgi/chrome/common/foo.ico',
                         links['shortcut icon'][0]['href'])

        # URL relative to the server root for icon config option
        self.env.config.set('project', 'icon', '/favicon.ico')
        links = chrome.prepare_request(req)['links']
        self.assertEqual('/favicon.ico', links['icon'][0]['href'])
        self.assertEqual('/favicon.ico', links['shortcut icon'][0]['href'])

        # Absolute URL for icon config option
        self.env.config.set('project', 'icon', 'http://example.com/favicon.ico')
        links = chrome.prepare_request(req)['links']
        self.assertEqual('http://example.com/favicon.ico',
                         links['icon'][0]['href'])
        self.assertEqual('http://example.com/favicon.ico',
                         links['shortcut icon'][0]['href'])
Пример #14
0
    def expand_macro(self, formatter, name, content, args=None):
        """
        Returns the outcome from macro.
        Supported arguments:

        - project: Name of the project to show status / provide follow buttons. Defaults to current project

        """
        req = formatter.req

        # Parse optional arguments
        if args is None:
            args = parse_args(content)
            if len(args) > 1:
                args = args[1]

        # Read optional project name - fallback to current
        project_name = self.env.project_identifier
        if args and 'project' in args:
            project_name = args.get('project', '').strip()

        # Load project id from db
        project_id = Project.get(env_name=project_name).id
        watchers, is_watching = self._get_status(req, project_id)

        # If user is already watching, do not show the block
        if is_watching:
            return tag.div('')

        # Show macro only when user has permission to view timeline
        if name not in self.macros or 'TIMELINE_VIEW' not in req.perm or not project_id:
            # Return default content / instructions
            return tag.div(
                tag.h2(_('Follow project'), **{'class': 'title'}),
                tag.p(_('Project cannot be found or no permission to follow it')),
                **{'class': 'watch'}
            )

        # For anonymous users, advice login/registering
        if req.authname == 'anonymous':
            return tag.div(
                tag.h2(_('Follow project'), **{'class': 'title'}),
                tag.p(_('Only registered users can follow the project activity. ')),
                tag.p(tag.a('Please login or register to service first', href=req.href('../home/user'))),
                **{'class': 'watch'}
            )

        # Return rendered HTML with JS attached to it
        data = {
            'project_id': project_id,
            'env_name': self.env.project_identifier,
            'project_name': project_name
        }

        chrome = Chrome(self.env)
        stream = chrome.render_template(req, 'multiproject_watch.html', data, fragment=True)
        if req.form_token:
            stream |= chrome._add_form_token(req.form_token)

        return stream
Пример #15
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        stats_provider, kwargs, is_preview_with_self = self._parse_macro_content(content, req)

        if is_preview_with_self:
            # previewing newticket, without a number but with a reference
            # to current ticket number; show a helpful message
            return tag.div('Progress meter will be inserted here in final ticket')

        # Create & execute the query string
        qstr = '&'.join(['%s=%s' % item
                               for item in kwargs.iteritems()])
        query = Query.from_string(self.env, qstr, max=0)
        try:
            constraints = query.constraints[0]
        except IndexError:
            constraints = query.constraints

        # Calculate stats
        qres = query.execute(req)
        tickets = apply_ticket_permissions(self.env, req, qres)

        stats = get_ticket_stats(stats_provider, tickets)
        stats_data = query_stats_data(req, stats, constraints)

        # ... and finally display them
        add_stylesheet(req, 'common/css/roadmap.css')
        chrome = Chrome(self.env)
        return chrome.render_template(req, 'progressmeter.html', stats_data,
                                      fragment=True)
Пример #16
0
class ChromeTestCase2(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub(path=tempfile.mkdtemp())
        self.chrome = Chrome(self.env)

    def tearDown(self):
        shutil.rmtree(self.env.path)

    def test_malicious_filename_raises(self):
        req = Request(path_info='/chrome/site/../conf/trac.ini')
        self.assertTrue(self.chrome.match_request(req))
        self.assertRaises(TracError, self.chrome.process_request, req)

    def test_empty_shared_htdocs_dir_raises_file_not_found(self):
        req = Request(path_info='/chrome/shared/trac_logo.png')
        self.assertEqual('', self.chrome.shared_htdocs_dir)
        self.assertTrue(self.chrome.match_request(req))
        from trac.web.api import HTTPNotFound
        self.assertRaises(HTTPNotFound, self.chrome.process_request, req)

    def test_shared_htdocs_dir_file_is_found(self):
        from trac.web.api import RequestDone
        def send_file(path, mimetype):
            raise RequestDone
        req = Request(path_info='/chrome/shared/trac_logo.png',
                      send_file=send_file)
        shared_htdocs_dir = os.path.join(self.env.path, 'chrome', 'shared')
        os.makedirs(shared_htdocs_dir)
        create_file(os.path.join(shared_htdocs_dir, 'trac_logo.png'))
        self.env.config.set('inherit', 'htdocs_dir', shared_htdocs_dir)
        self.assertTrue(self.chrome.match_request(req))
        self.assertRaises(RequestDone, self.chrome.process_request, req)
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        stats_provider, kwargs, preview = self._parse_macro_content(content, req)

        # Create & execute the query string
        qstr = '&'.join(['%s=%s' % item
                               for item in kwargs.iteritems()])
        query = Query.from_string(self.env, qstr)
        try:
            # XXX: simplification, may cause problems with more complex queries
            constraints = query.constraints[0]
        except IndexError:
            constraints = {}

        # Calculate stats
        qres = query.execute(req)
        tickets = apply_ticket_permissions(self.env, req, qres)

        stats = get_ticket_stats(stats_provider, tickets)
        stats_data = query_stats_data(req, stats, constraints)

        # ... and finally display them
        add_stylesheet(req, 'common/css/roadmap.css')
        chrome = Chrome(self.env)
        stats_data.update({'preview': preview})     # displaying a preview?
        return chrome.render_template(req, 'progressmeter.html', stats_data,
                                      fragment=True)
Пример #18
0
    def expand_macro(self, formatter, name, content):
        
        data=dict(user_profiles=[], user_profile_fields={})
        rendered_result=""
        content_args={}
        layout_args={}
        user_profile_templates=[]     
        
        # collecting arguments
        if content:    
            for i, macro_args in enumerate( content.split('|') ):
                if i == 0:
                    content_args = MacroArguments( macro_args )
                    continue
                if i == 1: 
                    layout_args = MacroArguments( macro_args )
                    break
            
            # extracting userProfile attrs 
            if len(content_args)>0:
                user_profile_templates.append(User(**content_args))
                
            if len(content_args.get_list_args())>0:
                for list_item in content_args.get_list_args():
                    user_profile_templates.append(User( **MacroArguments(list_item[1:len(list_item)-1])))
        
        # adding profiles fields description 
        data['user_profile_fields'].update(UserProfileManager(self.env).get_user_profile_fields(ignore_internal=True))

        # removing picture_href
        data['user_profile_fields'].pop('picture_href')
        
        
        def inline_wiki_to_html(text):
            return wiki_to_html(text, self.env, formatter.req)
        data['wiki_to_html'] = inline_wiki_to_html
        
        # grabbing users
        if len(user_profile_templates)>0:
            data['user_profiles'] = UserManager(self.env).search_users(user_profile_templates)
        else:
            data['user_profiles'] = UserManager(self.env).get_active_users()
        
        data['cells']=list(self._get_cells(data['user_profiles']))
        
        # add stylesheet&script
        add_stylesheet(formatter.req,'tracusermanager/css/macros_um_profile.css')
        add_script(formatter.req,'tracusermanager/js/macros_um_profile.js')
        
        # render template
        template = Chrome(self.env).load_template('macro_um_profile.html',method='xhtml')
        data = Chrome(self.env).populate_data(formatter.req, {'users':data})

        rendered_result = template.generate(**data)

        # wrap everything 
        if len(layout_args)>0:
            rendered_result= html.div(rendered_result, **layout_args)

        return rendered_result
Пример #19
0
    def render_property_diff(self, req, ticket, field, old, new,
                              resource_new=None):
        "Version for Trac 0.11"
        rendered = None
        # per type special rendering of diffs
        type_ = None
        for f in ticket.fields:
            if f['name'] == field:
                type_ = f['type']
                break
        if type_ == 'checkbox':
            rendered = new == '1' and "set" or "unset"
        elif type_ == 'textarea':
            if not resource_new:
                rendered = _('modified')
            else:
                href = get_resource_url(self.env, resource_new, req.href,
                                        action='diff')
                rendered = tag('modified (', tag.a('diff', href=href), ')')

        # per name special rendering of diffs
        old_list, new_list = None, None
        render_elt = lambda x: x
        sep = ', '
        if field == 'cc':
            chrome = Chrome(self.env)
            old_list, new_list = chrome.cc_list(old), chrome.cc_list(new)
            if not (Chrome(self.env).show_email_addresses or
                    'EMAIL_VIEW' in req.perm(resource_new or ticket.resource)):
                render_elt = obfuscate_email_address
        elif field == 'keywords':
            old_list, new_list = (old or '').split(), new.split()
            sep = ' '
        if (old_list, new_list) != (None, None):
            added = [tag.em(render_elt(x)) for x in new_list
                     if x not in old_list]
            remvd = [tag.em(render_elt(x)) for x in old_list
                     if x not in new_list]
            added = added and tag(separated(added, sep), " added")
            remvd = remvd and tag(separated(remvd, sep), " removed")
            if added or remvd:
                rendered = tag(added, added and remvd and '; ', remvd)
                return rendered
        if field in ('reporter', 'owner'):
            if not (Chrome(self.env).show_email_addresses or
                    'EMAIL_VIEW' in req.perm(resource_new or ticket.resource)):
                old = obfuscate_email_address(old)
                new = obfuscate_email_address(new)
        # Added by MS
        if field == 'attachment':
            rendered = tag(tag.em(new), " added")
        # changed 'if' to 'elif':
        elif old and not new:
            rendered = tag(tag.em(old), " deleted")
        elif new and not old:
            rendered = tag("set to ", tag.em(new))
        elif old and new:
            rendered = tag("changed from ", tag.em(old),
                            " to ", tag.em(new))
        return rendered
Пример #20
0
    def matches(self, event):
        if event.realm != 'ticket':
            return
        if event.category not in ('created', 'changed', 'attachment added',
                                  'attachment deleted'):
            return

        # CC field is stored as comma-separated string. Parse to set.
        chrome = Chrome(self.env)
        to_set = lambda cc: set(chrome.cc_list(cc))
        cc_set = to_set(event.target['cc'] or '')

        # Harvest previous CC field
        if 'fields' in event.changes and 'cc' in event.changes['fields']:
            cc_set.update(to_set(event.changes['fields']['cc']['old']))

        matcher = RecipientMatcher(self.env)
        klass = self.__class__.__name__
        sids = set()
        for cc in cc_set:
            recipient = matcher.match_recipient(cc)
            if not recipient:
                continue
            sid, auth, addr = recipient

            # Default subscription
            for s in self.default_subscriptions():
                yield (s[0], s[1], sid, auth, addr, s[2], s[3], s[4])
            if sid:
                sids.add((sid,auth))

        for s in Subscription.find_by_sids_and_class(self.env, sids, klass):
            yield s.subscription_tuple()
Пример #21
0
    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.
        """
        if filename == "query.html" and self.inject_query:
            self.geoticket()  # sanity check
            chrome = Chrome(self.env)
            variables = ("center_location", "radius")
            _data = dict([(i, data.get(i)) for i in variables])

            # georegions
            _data["geo_column_label"] = None
            _data["regions"] = None
            if self.env.is_component_enabled(GeoRegions):
                georegions = GeoRegions(self.env)
                if georegions.enabled():
                    regions = georegions.regions()
                    if regions:
                        column, regions = regions
                        _data["geo_column_label"] = column
                        _data["regions"] = regions
                        _data["region"] = req.args.get("region")

            template = chrome.load_template("geoquery.html")
            stream |= Transformer("//fieldset[@id='columns']").after(template.generate(**_data))

        return stream
Пример #22
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        stats_provider, kwargs = self._parse_macro_content(content, req)

        # Create & execute the query string
        qstr = '&'.join(['%s=%s' % item
                               for item in kwargs.iteritems()])
        query = Query.from_string(self.env, qstr, max=0)
        try:
            constraints = query.constraints[0]
        except IndexError:
            constraints = query.constraints

        # Calculate stats
        qres = query.execute(req)
        tickets = apply_ticket_permissions(self.env, req, qres)

        stats = get_ticket_stats(stats_provider, tickets)
        stats_data = query_stats_data(req, stats, constraints)

        # ... and finally display them
        add_stylesheet(req, 'common/css/roadmap.css')
        chrome = Chrome(self.env)
        return chrome.render_template(req, 'progressmeter.html', stats_data,
                                      fragment=True)
Пример #23
0
    def process_request(self, req):
        req.perm.assert_permission('STRACTISTICS_VIEW')
        add_stylesheet(req, 'hw/css/stractistics.css')
        
        #Reading options from trac.ini
        config = util.read_config_options(self.env.config)
        
        #Patch for Trac 0.11
        if trac.__version__.find('0.11') != -1:
            chrome = Chrome(self.env)
            chrome.populate_hdf(req)
                
        #Populating our context navigation bar.
        strac_ref = req.href.stractistics()
        links = []
        for elem in self.sections:
            links.append((elem[0], "/".join([strac_ref, elem[1]])))
        req.hdf['section_links'] = links
        
        db = self.env.get_db_cnx()
        module = req.args.get('module', None)
        if module is not None and module == 'user_reports':
            template, content_type = user_reports.user_reports(req, 
                                                               config, 
                                                               db)
        else:
            template, content_type = global_reports.global_reports(req, 
                                                                   config, 
                                                                   db)
        return template, content_type

    
                
Пример #24
0
    def test_attributes_preserved_in_navigation_item(self):
        class TestNavigationContributor1(Component):
            implements(INavigationContributor)
            def get_active_navigation_item(self, req):
                return None
            def get_navigation_items(self, req):
                yield 'mainnav', 'test1', \
                      tag.a('Test 1', href='test1', target='blank')
        class TestNavigationContributor2(Component):
            implements(INavigationContributor)
            def get_active_navigation_item(self, req):
                return None
            def get_navigation_items(self, req):
                yield 'mainnav', 'test2', \
                      tag.a('Test 2', href='test2', target='blank')
        req = Request(abs_href=Href('http://example.org/trac.cgi'),
                      href=Href('/trac.cgi'), base_path='/trac.cgi',
                      path_info='/',
                      add_redirect_listener=lambda listener: None)
        self.env.config.set('mainnav', 'test1.label', 'Test One')
        self.env.config.set('mainnav', 'test2.label', 'Test Two')
        self.env.config.set('mainnav', 'test2.href', 'testtwo')

        chrome = Chrome(self.env)
        items = chrome.prepare_request(req)['nav']['mainnav']

        item = self._get_navigation_item(items, 'test1')
        self.assertEqual(str(tag.a('Test One', href='test1', target='blank')),
                         str(item['label']))
        item = self._get_navigation_item(items, 'test2')
        self.assertEqual(str(tag.a('Test Two', href='testtwo',
                                   target='blank')),
                         str(item['label']))
Пример #25
0
    def _render_editor(self, req, milestone):
        data = {
            'milestone': milestone,
            'datetime_hint': get_datetime_format_hint(req.lc_time),
            'default_due': self.get_default_due(req),
            'milestone_groups': [],
        }

        if milestone.exists:
            req.perm(milestone.resource).require('MILESTONE_MODIFY')
            milestones = [m for m in Milestone.select(self.env)
                          if m.name != milestone.name
                          and 'MILESTONE_VIEW' in req.perm(m.resource)]
            data['milestone_groups'] = group_milestones(milestones,
                'TICKET_ADMIN' in req.perm)
            data['num_open_tickets'] = milestone \
                                       .get_num_tickets(exclude_closed=True)
            data['retarget_to'] = self.default_retarget_to
        else:
            req.perm(milestone.resource).require('MILESTONE_CREATE')
            if milestone.name:
                add_notice(req, _("Milestone %(name)s does not exist. You can"
                                  " create it here.", name=milestone.name))

        chrome = Chrome(self.env)
        chrome.add_jquery_ui(req)
        chrome.add_wiki_toolbars(req)
        add_stylesheet(req, 'common/css/roadmap.css')
        return 'milestone_edit.html', data, None
Пример #26
0
    def filter_stream(self, req, method, filename, stream, data):
        chrome = Chrome(self.env)

        if req.path_info.startswith('/milestone') \
            and req.args.get('action') in ['edit', 'new'] \
            and 'max_level' not in data:
            milestone = data.get('milestone')
            levels = IttecoMilestoneAdminPanel(self.env).milestone_levels
            mydata ={'structured_milestones':StructuredMilestone.select(self.env),
                  'max_level':  levels and len(levels)-1 or 0,
                  'milestone_name' : milestone and milestone.parent or None,
                  'field_name' : 'parent'}
            stream |=Transformer('//*[@id="edit"]/fieldset').append(
                chrome.render_template(req, 'itteco_milestones_dd.html', mydata, fragment=True))
            
        if 'ticket' in data:
            tkt = data['ticket']
            mydata ={
                'structured_milestones':StructuredMilestone.select(self.env),
                'milestone_name': data['ticket']['milestone'],
                'field_name' : 'field_milestone',
                'hide_completed' : not ( tkt.exists and 'TICKET_ADMIN' in req.perm(tkt.resource))
            }
            req.chrome.setdefault('ctxtnav',[]).insert(
                -1, 
                tag.a(
                    _('Go To Whiteboard'), 
                    href=req.href.whiteboard('team_tasks', data['ticket']['milestone'] or 'none')
                )
            )
            stream |=Transformer('//*[@id="field-milestone"]').replace(
                chrome.render_template(req, 'itteco_milestones_dd.html', mydata, fragment=True))
        return stream
    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 '')
Пример #28
0
    def filter_stream(self, req, method, filename, stream, data):
        action = req.args.get('action', '')
        
        if filename == 'browser.html' and action == 'edit':
            req.perm.require('REPOSITORY_MODIFY')
            # NB TracBrowserOps already inserts javascript and css we need
            # So only add css/javascript needed solely by the editor
            
            if data['file'] and data['file']['preview']['rendered']:
                max_edit_size = self.max_edit_size
                data['max_edit_size'] = max_edit_size
                
                # Discard rendered table, replace with textarea of file contents
                # This means reading the file from the repository again
                # N.B. If a file is rendered as something other than a table
                # e.g. due to PreCodeBrowserPlugin this code won't trigger
                
                # Retrieve the same node that BrowserModule.process_request() 
                # used to render the preview.
                # At this point reponame has been removed from data['path']
                # and repos has already been determined
                repos = data['repos']
                path = data['path']
                rev = data['rev']
                node = repos.get_node(path, rev)
                
                # If node is too large then don't allow editing, abort
                if max_edit_size > 0 and node.content_length > max_edit_size:
                    return stream
                
                # Open the node and read it
                node_file = node.get_content() 
                node_data = node_file.read()

                # Discover the mime type and character encoding of the node
                # Try in order
                #  - svn:mime-type property
                #  - detect from file name and content (BOM)
                #  - use configured default for Trac
                mime_view = Mimeview(self.env)
                mime_type = node.content_type \
                            or mime_view.get_mimetype(node.name, node_data) \
                            or 'text/plain'
                encoding = mime_view.get_charset(node_data, mime_type)
                
                # Populate template data
                content = mime_view.to_unicode(node_data, mime_type, encoding)
                data['file_content'] = content               
                data['file_encoding'] = encoding
                
                # Replace the already rendered preview with a form and textarea
                bsops_stream = Chrome(self.env).render_template(req,
                        'file_edit.html', data, fragment=True)
                transf = Transformer('//div[@id="preview"]'
                                     '/table[@class="code"]')
                stream |=  transf.replace(
                        bsops_stream.select('//div[@id="bsop_edit"]'))
        return stream
Пример #29
0
 def content(self, req, ticket):
     tm = TicketMover(self.env)
     projects = tm.projects(req.authname)
     chrome = Chrome(self.env)
     template = chrome.load_template('ticketmover-sidebar.html')
     data = {'projects': projects,
             'req': req,
             'ticket': ticket}
     return template.generate(**data)
Пример #30
0
 def diff_cc(self, old, new):
     chrome = Chrome(self.env)
     oldcc = chrome.cc_list(old)
     newcc = chrome.cc_list(new)
     added = [self.format_author(x)
              for x in newcc if x and x not in oldcc]
     rmved = [self.format_author(x)
              for x in oldcc if x and x not in newcc]
     return added, rmved
Пример #31
0
    def render_admin_panel(self, req, cat, page, path_info):
        valid_default_handlers = [
            handler.__class__.__name__ for handler in self.request_handlers
            if is_valid_default_handler(handler)
        ]
        if Locale:
            locale_ids = get_available_locales()
            locales = [Locale.parse(locale) for locale in locale_ids]
            # don't use str(locale) to prevent storing expanded locale
            # identifier, see #11258
            languages = sorted((id, locale.display_name)
                               for id, locale in zip(locale_ids, locales))
        else:
            locale_ids, locales, languages = [], [], []

        if req.method == 'POST':
            for option in ('name', 'url', 'descr'):
                self.config.set('project', option, req.args.get(option))

            default_handler = req.args.get('default_handler')
            self.config.set('trac', 'default_handler', default_handler)

            default_timezone = req.args.get('default_timezone')
            if default_timezone not in all_timezones:
                default_timezone = ''
            self.config.set('trac', 'default_timezone', default_timezone)

            default_language = req.args.get('default_language')
            if default_language not in locale_ids:
                default_language = ''
            self.config.set('trac', 'default_language', default_language)

            default_date_format = req.args.get('default_date_format')
            if default_date_format != 'iso8601':
                default_date_format = ''
            self.config.set('trac', 'default_date_format', default_date_format)

            default_dateinfo_format = req.args.get('default_dateinfo_format')
            if default_dateinfo_format not in ('relative', 'absolute'):
                default_dateinfo_format = 'relative'
            self.config.set('trac', 'default_dateinfo_format',
                            default_dateinfo_format)

            _save_config(self.config, req, self.log)
            req.redirect(req.href.admin(cat, page))

        default_handler = self.config.get('trac', 'default_handler')
        default_timezone = self.config.get('trac', 'default_timezone')
        default_language = self.config.get('trac', 'default_language')
        default_date_format = self.config.get('trac', 'default_date_format')
        default_dateinfo_format = self.config.get('trac',
                                                  'default_dateinfo_format')

        data = {
            'default_handler': default_handler,
            'valid_default_handlers': sorted(valid_default_handlers),
            'default_timezone': default_timezone,
            'timezones': all_timezones,
            'has_pytz': pytz is not None,
            'default_language': default_language.replace('-', '_'),
            'languages': languages,
            'default_date_format': default_date_format,
            'default_dateinfo_format': default_dateinfo_format,
            'has_babel': Locale is not None,
        }
        Chrome(self.env).add_textarea_grips(req)
        return 'admin_basics.html', data
Пример #32
0
    def dispatch(self, req):
        """Find a registered handler that matches the request and let
        it process it.

        In addition, this method initializes the data dictionary
        passed to the the template and adds the web site chrome.
        """
        self.log.debug('Dispatching %r', req)
        chrome = Chrome(self.env)

        try:
            # Select the component that should handle the request
            chosen_handler = None
            for handler in self._request_handlers.values():
                if handler.match_request(req):
                    chosen_handler = handler
                    break
            if not chosen_handler and req.path_info in ('', '/'):
                chosen_handler = self._get_valid_default_handler(req)
            # pre-process any incoming request, whether a handler
            # was found or not
            self.log.debug("Chosen handler is %s", chosen_handler)
            chosen_handler = self._pre_process_request(req, chosen_handler)
            if not chosen_handler:
                if req.path_info.endswith('/'):
                    # Strip trailing / and redirect
                    target = unicode_quote(req.path_info.rstrip('/'))
                    if req.query_string:
                        target += '?' + req.query_string
                    req.redirect(req.href + target, permanent=True)
                raise HTTPNotFound('No handler matched request to %s',
                                   req.path_info)

            req.callbacks['chrome'] = partial(chrome.prepare_request,
                                              handler=chosen_handler)

            # Protect against CSRF attacks: we validate the form token
            # for all POST requests with a content-type corresponding
            # to form submissions
            if req.method == 'POST':
                ctype = req.get_header('Content-Type')
                if ctype:
                    ctype, options = cgi.parse_header(ctype)
                if ctype in ('application/x-www-form-urlencoded',
                             'multipart/form-data') and \
                        req.args.get('__FORM_TOKEN') != req.form_token:
                    if self.env.secure_cookies and req.scheme == 'http':
                        msg = _('Secure cookies are enabled, you must '
                                'use https to submit forms.')
                    else:
                        msg = _('Do you have cookies enabled?')
                    raise HTTPBadRequest(
                        _('Missing or invalid form token.'
                          ' %(msg)s', msg=msg))

            # Process the request and render the template
            resp = chosen_handler.process_request(req)
            if resp:
                resp = self._post_process_request(req, *resp)
                template, data, metadata, method = resp
                if 'hdfdump' in req.args:
                    req.perm.require('TRAC_ADMIN')
                    # debugging helper - no need to render first
                    out = io.BytesIO()
                    pprint(
                        {
                            'template': template,
                            'metadata': metadata,
                            'data': data
                        }, out)
                    req.send(out.getvalue(), 'text/plain')
                self.log.debug("Rendering response with template %s", template)
                iterable = chrome.use_chunked_encoding
                if isinstance(metadata, dict):
                    iterable = metadata.setdefault('iterable', iterable)
                    content_type = metadata.get('content_type')
                else:
                    content_type = metadata
                output = chrome.render_template(req,
                                                template,
                                                data,
                                                metadata,
                                                iterable=iterable,
                                                method=method)
                # TODO (1.5.1) remove iterable and method parameters
                req.send(output, content_type or 'text/html')
            else:
                self.log.debug("Empty or no response from handler. "
                               "Entering post_process_request.")
                self._post_process_request(req)
        except RequestDone:
            raise
        except Exception as e:
            # post-process the request in case of errors
            err = sys.exc_info()
            try:
                self._post_process_request(req)
            except RequestDone:
                pass
            except TracError as e2:
                self.log.warning(
                    "Exception caught while post-processing"
                    " request: %s", exception_to_unicode(e2))
            except Exception as e2:
                if not (type(e) is type(e2) and e.args == e2.args):
                    self.log.error(
                        "Exception caught while post-processing"
                        " request: %s", exception_to_unicode(e2,
                                                             traceback=True))
            if isinstance(e, PermissionError):
                raise HTTPForbidden(e)
            if isinstance(e, ResourceNotFound):
                raise HTTPNotFound(e)
            if isinstance(e, NotImplementedError):
                tb = traceback.extract_tb(err[2])[-1]
                self.log.warning("%s caught from %s:%d in %s: %s",
                                 e.__class__.__name__, tb[0], tb[1], tb[2],
                                 to_unicode(e) or "(no message)")
                raise HTTPInternalServerError(TracNotImplementedError(e))
            if isinstance(e, TracError):
                raise HTTPInternalServerError(e)
            raise err[0], err[1], err[2]
Пример #33
0
def send_internal_error(env, req, exc_info):
    if env:
        env.log.error("[%s] Internal Server Error: %r, referrer %r%s",
                      req.remote_addr, req, req.environ.get('HTTP_REFERER'),
                      exception_to_unicode(exc_info[1], traceback=True))
    message = exception_to_unicode(exc_info[1])
    traceback = get_last_traceback()

    frames, plugins, faulty_plugins, interface_custom = [], [], [], []
    th = 'http://trac-hacks.org'
    has_admin = False
    try:
        has_admin = 'TRAC_ADMIN' in req.perm
    except Exception:
        pass

    tracker = default_tracker
    tracker_args = {}
    if has_admin and not isinstance(exc_info[1], MemoryError):
        # Collect frame and plugin information
        frames = get_frame_info(exc_info[2])
        if env:
            plugins = [
                p for p in get_plugin_info(env)
                if any(c['enabled'] for m in p['modules'].itervalues()
                       for c in m['components'].itervalues())
            ]
            match_plugins_to_frames(plugins, frames)

            # Identify the tracker where the bug should be reported
            faulty_plugins = [p for p in plugins if 'frame_idx' in p]
            faulty_plugins.sort(key=lambda p: p['frame_idx'])
            if faulty_plugins:
                info = faulty_plugins[0]['info']
                home_page = info.get('home_page', '')
                if 'trac' in info:
                    tracker = info['trac']
                elif urlparse(home_page).netloc == urlparse(th).netloc:
                    tracker = th
                    plugin_name = info.get('home_page', '').rstrip('/') \
                                                           .split('/')[-1]
                    tracker_args = {'component': plugin_name}
        interface_custom = Chrome(env).get_interface_customization_files()

    def get_description(_):
        if env and has_admin:
            sys_info = "".join(
                "|| '''`%s`''' || `%s` ||\n" %
                (k, (v.replace('\n', '` [[br]] `') if v else _('N/A')))
                for k, v in env.system_info)
            sys_info += "|| '''`jQuery`''' || `#JQUERY#` ||\n" \
                        "|| '''`jQuery UI`''' || `#JQUERYUI#` ||\n" \
                        "|| '''`jQuery Timepicker`''' || `#JQUERYTP#` ||\n"
            enabled_plugins = "".join("|| '''`%s`''' || `%s` ||\n" %
                                      (p['name'], p['version'] or _('N/A'))
                                      for p in plugins)
            files = Chrome(env).get_interface_customization_files().items()
            interface_files = "".join("|| **%s** || %s ||\n" %
                                      (k, ", ".join("`%s`" % f for f in v))
                                      for k, v in sorted(files))
        else:
            sys_info = _("''System information not available''\n")
            enabled_plugins = _("''Plugin information not available''\n")
            interface_files = _("''Interface customization information not "
                                "available''\n")
        return _("""\
==== How to Reproduce ====

While doing a %(method)s operation on `%(path_info)s`, Trac issued an internal error.

''(please provide additional details here)''

Request parameters:
{{{
%(req_args)s
}}}

User agent: `#USER_AGENT#`

==== System Information ====
%(sys_info)s
==== Enabled Plugins ====
%(enabled_plugins)s
==== Interface Customization ====
%(interface_customization)s
==== Python Traceback ====
{{{
%(traceback)s}}}""",
                 method=req.method,
                 path_info=req.path_info,
                 req_args=pformat(req.args),
                 sys_info=sys_info,
                 enabled_plugins=enabled_plugins,
                 interface_customization=interface_files,
                 traceback=to_unicode(traceback))

    # Generate the description once in English, once in the current locale
    description_en = get_description(lambda s, **kw: safefmt(s, kw))
    try:
        description = get_description(_)
    except Exception:
        description = description_en

    data = {
        'title': 'Internal Error',
        'type': 'internal',
        'message': message,
        'traceback': traceback,
        'frames': frames,
        'shorten_line': shorten_line,
        'repr': safe_repr,
        'plugins': plugins,
        'faulty_plugins': faulty_plugins,
        'interface': interface_custom,
        'tracker': tracker,
        'tracker_args': tracker_args,
        'description': description,
        'description_en': description_en
    }

    Chrome(env).add_jquery_ui(req)
    try:
        req.send_error(exc_info, status=500, env=env, data=data)
    except RequestDone:
        pass
Пример #34
0
def MockRequest(env, **kwargs):
    """Request object for testing. Keyword arguments populate an
    `environ` dictionary and the callbacks.

    If `authname` is specified in a keyword arguments a `PermissionCache`
    object is created, otherwise if `authname` is not specified or is
    `None` a `MockPerm` object is used and the `authname` is set to
    'anonymous'.

    The following keyword arguments are commonly used:
    :keyword args: dictionary of request arguments
    :keyword authname: the name of the authenticated user, or 'anonymous'
    :keyword method: the HTTP request method
    :keyword path_info: the request path inside the application

    Additionally `cookie`, `format`, `language`, `lc_time`, `locale`, 
    `remote_addr`, `remote_user`, `script_name`, `server_name`, `server_port`
    and `tz` can be specified as keyword arguments.

    :since: 1.0.11
    """

    authname = kwargs.get('authname')
    if authname is None:
        authname = 'anonymous'
        perm = MockPerm()
    else:
        perm = PermissionCache(env, authname)

    if 'arg_list' in kwargs:
        arg_list = kwargs['arg_list']
        args = arg_list_to_args(arg_list)
    else:
        args = _RequestArgs()
        args.update(kwargs.get('args', {}))
        arg_list = [(name, value) for name in args
                                  for value in args.getlist(name)]

    environ = {
        'trac.base_url': env.abs_href(),
        'wsgi.url_scheme': 'http',
        'HTTP_ACCEPT_LANGUAGE': kwargs.get('language', ''),
        'HTTP_COOKIE': kwargs.get('cookie', ''),
        'PATH_INFO': kwargs.get('path_info', '/'),
        'REQUEST_METHOD': kwargs.get('method', 'GET'),
        'REMOTE_ADDR': kwargs.get('remote_addr', '127.0.0.1'),
        'REMOTE_USER': kwargs.get('remote_user', authname),
        'SCRIPT_NAME': kwargs.get('script_name', '/trac.cgi'),
        'SERVER_NAME': kwargs.get('server_name', 'localhost'),
        'SERVER_PORT': kwargs.get('server_port', '80'),
    }

    status_sent = []
    headers_sent = {}
    response_sent = io.BytesIO()

    def start_response(status, headers, exc_info=None):
        status_sent.append(status)
        headers_sent.update(dict(headers))
        return response_sent.write

    req = Mock(Request, environ, start_response)
    req.status_sent = status_sent
    req.headers_sent = headers_sent
    req.response_sent = response_sent

    req.callbacks.update({
        'arg_list': lambda req: arg_list,
        'args': lambda req: args,
        'authname': lambda req: authname,
        'chrome': Chrome(env).prepare_request,
        'form_token': lambda req: kwargs.get('form_token', 0),
        'languages': Request._parse_languages,
        'lc_time': lambda req: kwargs.get('lc_time', locale_en),
        'locale': lambda req: kwargs.get('locale'),
        'incookie': Request._parse_cookies,
        'perm': lambda req: perm,
        'session': lambda req: Session(env, req),
        'tz': lambda req: kwargs.get('tz', utc),
        'use_xsendfile': False,
        'xsendfile_header': None,
        '_inheaders': Request._parse_headers
    })

    return req
Пример #35
0
    def render_ticket_action_control(self, req, ticket, action):

        self.log.debug('render_ticket_action_control: action "%s"', action)

        this_action = self.actions[action]
        label = this_action['label']
        operations = this_action['operations']
        ticket_owner = ticket._old.get('owner', ticket['owner'])
        ticket_status = ticket._old.get('status', ticket['status'])
        next_status = this_action['newstate']
        author = get_reporter_id(req, 'author')
        author_info = partial(Chrome(self.env).authorinfo, req,
                              resource=ticket.resource)
        format_author = partial(Chrome(self.env).format_author, req,
                                resource=ticket.resource)
        formatted_current_owner = author_info(ticket_owner)
        exists = ticket_status is not None

        ticket_system = TicketSystem(self.env)
        control = []  # default to nothing
        hints = []
        if 'reset_workflow' in operations:
            control.append(_("from invalid state"))
            hints.append(_("Current state no longer exists"))
        if 'del_owner' in operations:
            hints.append(_("The ticket will be disowned"))
        if 'set_owner' in operations or 'may_set_owner' in operations:
            owners = self.get_allowed_owners(req, ticket, this_action)

            if 'set_owner' in operations:
                default_owner = author
            elif 'may_set_owner' in operations:
                if not exists:
                    default_owner = ticket_system.default_owner
                else:
                    default_owner = ticket_owner or None
                if owners is not None and default_owner not in owners:
                    owners.insert(0, default_owner)
            else:
                # Protect against future modification for case that another
                # operation is added to the outer conditional
                raise AssertionError(operations)

            id = 'action_%s_reassign_owner' % action

            if not owners:
                owner = req.args.get(id, default_owner)
                control.append(
                    tag_("to %(owner)s",
                         owner=tag.input(type='text', id=id, name=id,
                                         value=owner)))
                if not exists or ticket_owner is None:
                    hints.append(_("The owner will be the specified user"))
                else:
                    hints.append(tag_("The owner will be changed from "
                                      "%(current_owner)s to the specified "
                                      "user",
                                      current_owner=formatted_current_owner))
            elif len(owners) == 1:
                owner = tag.input(type='hidden', id=id, name=id,
                                  value=owners[0])
                formatted_new_owner = author_info(owners[0])
                control.append(tag_("to %(owner)s",
                                    owner=tag(formatted_new_owner, owner)))
                if not exists or ticket_owner is None:
                    hints.append(tag_("The owner will be %(new_owner)s",
                                      new_owner=formatted_new_owner))
                elif ticket['owner'] != owners[0]:
                    hints.append(tag_("The owner will be changed from "
                                      "%(current_owner)s to %(new_owner)s",
                                      current_owner=formatted_current_owner,
                                      new_owner=formatted_new_owner))
            else:
                selected_owner = req.args.get(id, default_owner)
                control.append(tag_("to %(owner)s", owner=tag.select(
                    [tag.option(text, value=value if value is not None else '',
                                selected=(value == selected_owner or None))
                     for text, value in sorted((format_author(owner), owner)
                                                for owner in owners)],
                    id=id, name=id)))
                if not exists or ticket_owner is None:
                    hints.append(_("The owner will be the selected user"))
                else:
                    hints.append(tag_("The owner will be changed from "
                                      "%(current_owner)s to the selected user",
                                      current_owner=formatted_current_owner))
        elif 'set_owner_to_self' in operations:
            formatted_author = author_info(author)
            if not exists or ticket_owner is None:
                hints.append(tag_("The owner will be %(new_owner)s",
                                  new_owner=formatted_author))
            elif ticket_owner != author:
                hints.append(tag_("The owner will be changed from "
                                  "%(current_owner)s to %(new_owner)s",
                                  current_owner=formatted_current_owner,
                                  new_owner=formatted_author))
            elif ticket_status != next_status:
                hints.append(tag_("The owner will remain %(current_owner)s",
                                  current_owner=formatted_current_owner))
        if 'set_resolution' in operations:
            resolutions = [r.name for r in Resolution.select(self.env)]
            if 'set_resolution' in this_action:
                valid_resolutions = set(resolutions)
                resolutions = this_action['set_resolution']
                if any(x not in valid_resolutions for x in resolutions):
                    raise ConfigurationError(_(
                        "Your workflow attempts to set a resolution but uses "
                        "undefined resolutions (configuration issue, please "
                        "contact your Trac admin)."))
            if not resolutions:
                raise ConfigurationError(_(
                    "Your workflow attempts to set a resolution but none is "
                    "defined (configuration issue, please contact your Trac "
                    "admin)."))
            id = 'action_%s_resolve_resolution' % action
            if len(resolutions) == 1:
                resolution = tag.input(type='hidden', id=id, name=id,
                                       value=resolutions[0])
                control.append(tag_("as %(resolution)s",
                                    resolution=tag(resolutions[0],
                                                   resolution)))
                hints.append(tag_("The resolution will be set to %(name)s",
                                  name=resolutions[0]))
            else:
                selected_option = req.args.get(id,
                                               ticket_system.default_resolution)
                control.append(tag_("as %(resolution)s",
                                    resolution=tag.select(
                    [tag.option(x, value=x,
                                selected=(x == selected_option or None))
                     for x in resolutions],
                    id=id, name=id)))
                hints.append(_("The resolution will be set"))
        if 'del_resolution' in operations:
            hints.append(_("The resolution will be deleted"))
        if 'leave_status' in operations:
            if len(operations) == 1:
                control.append(tag_("as %(status)s", status=ticket_status))
                hints.append(tag_("The owner will remain %(current_owner)s",
                                  current_owner=formatted_current_owner)
                             if ticket_owner else
                             _("The ticket will remain with no owner"))
        elif next_status == '*':
            label = None  # Control won't be created
        elif ticket['status'] is None:  # New ticket
            hints.append(tag_("The status will be '%(name)s'",
                              name=next_status))
        elif next_status != ticket_status:
            hints.append(tag_("Next status will be '%(name)s'",
                              name=next_status))
        return (label, tag(separated(control, ' ')),
                tag(separated(hints, '. ', '.') if hints else ''))
Пример #36
0
    def _render_view(self, req, id):
        """Retrieve the report results and pre-process them for rendering."""
        r = Report(self.env, id)
        title, description, sql = r.title, r.description, r.query

        # If this is a saved custom query, redirect to the query module
        #
        # A saved query is either an URL query (?... or query:?...),
        # or a query language expression (query:...).
        #
        # It may eventually contain newlines, for increased clarity.
        #
        query = ''.join(line.strip() for line in sql.splitlines())
        if query and (query[0] == '?' or query.startswith('query:?')):
            query = query if query[0] == '?' else query[6:]
            report_id = 'report=%s' % id
            if 'report=' in query:
                if report_id not in query:
                    err = _(
                        'When specified, the report number should be '
                        '"%(num)s".',
                        num=id)
                    req.redirect(req.href.report(id, action='edit', error=err))
            else:
                if query[-1] != '?':
                    query += '&'
                query += report_id
            req.redirect(req.href.query() + quote_query_string(query))
        elif query.startswith('query:'):
            from trac.ticket.query import Query, QuerySyntaxError
            try:
                query = Query.from_string(self.env, query[6:], report=id)
            except QuerySyntaxError as e:
                req.redirect(
                    req.href.report(id, action='edit', error=to_unicode(e)))
            else:
                req.redirect(query.get_href(req.href))

        format = req.args.get('format')
        if format == 'sql':
            self._send_sql(req, id, title, description, sql)

        title = '{%i} %s' % (id, title)

        report_resource = Resource(self.realm, id)
        req.perm(report_resource).require('REPORT_VIEW')
        context = web_context(req, report_resource)

        page = req.args.getint('page', 1)
        default_max = {
            'rss': self.items_per_page_rss,
            'csv': 0,
            'tab': 0
        }.get(format, self.items_per_page)
        max = req.args.getint('max')
        limit = as_int(max, default_max, min=0)  # explict max takes precedence
        offset = (page - 1) * limit

        sort_col = req.args.get('sort', '')
        asc = req.args.getint('asc', 0, min=0, max=1)
        args = {}

        def report_href(**kwargs):
            """Generate links to this report preserving user variables,
            and sorting and paging variables.
            """
            params = args.copy()
            if sort_col:
                params['sort'] = sort_col
            if page != 1:
                params['page'] = page
            if max != default_max:
                params['max'] = max
            params.update(kwargs)
            params['asc'] = 1 if params.get('asc', asc) else None
            return req.href.report(id, params)

        data = {
            'action': 'view',
            'report': {
                'id': id,
                'resource': report_resource
            },
            'context': context,
            'title': title,
            'description': description,
            'max': limit,
            'args': args,
            'show_args_form': False,
            'message': None,
            'paginator': None,
            'report_href': report_href
        }

        try:
            args = self.get_var_args(req)
            sql = self.get_default_var_args(args, sql)
        except ValueError as e:
            data['message'] = _("Report failed: %(error)s", error=e)
            return 'report_view.html', data, None
        data.update({
            'args': args,
            'title': sub_vars(title, args),
            'description': sub_vars(description or '', args)
        })

        try:
            res = self.execute_paginated_report(req, id, sql, args, limit,
                                                offset)
        except TracError as e:
            data['message'] = _("Report failed: %(error)s", error=e)
        else:
            if len(res) == 2:
                e, sql = res
                data['message'] = \
                    tag_("Report execution failed: %(error)s %(sql)s",
                         error=tag.pre(exception_to_unicode(e)),
                         sql=tag(tag.hr(),
                                 tag.pre(sql, style="white-space: pre")))
        if data['message']:
            return 'report_view.html', data, None

        cols, results, num_items, missing_args, limit_offset = res
        need_paginator = limit > 0 and limit_offset
        need_reorder = limit_offset is None
        results = [list(row) for row in results]
        numrows = len(results)

        paginator = None
        if need_paginator:
            paginator = Paginator(results, page - 1, limit, num_items)
            data['paginator'] = paginator
            if paginator.has_next_page:
                add_link(req, 'next', report_href(page=page + 1),
                         _('Next Page'))
            if paginator.has_previous_page:
                add_link(req, 'prev', report_href(page=page - 1),
                         _('Previous Page'))

            pagedata = []
            shown_pages = paginator.get_shown_pages(21)
            for p in shown_pages:
                pagedata.append([
                    report_href(page=p), None,
                    str(p),
                    _('Page %(num)d', num=p)
                ])
            fields = ['href', 'class', 'string', 'title']
            paginator.shown_pages = [dict(zip(fields, p)) for p in pagedata]
            paginator.current_page = {
                'href': None,
                'class': 'current',
                'string': str(paginator.page + 1),
                'title': None
            }
            numrows = paginator.num_items

        # Place retrieved columns in groups, according to naming conventions
        #  * _col_ means fullrow, i.e. a group with one header
        #  * col_ means finish the current group and start a new one

        field_labels = TicketSystem(self.env).get_ticket_field_labels()

        header_groups = [[]]
        for idx, col in enumerate(cols):
            if col in field_labels:
                title = field_labels[col]
            else:
                title = col.strip('_').capitalize()
            header = {
                'col': col,
                'title': title,
                'hidden': False,
                'asc': None,
            }

            if col == sort_col:
                if asc:
                    data['asc'] = asc
                data['sort'] = sort_col
                header['asc'] = bool(asc)
                if not paginator and need_reorder:
                    # this dict will have enum values for sorting
                    # and will be used in sortkey(), if non-empty:
                    sort_values = {}
                    if sort_col in ('status', 'resolution', 'priority',
                                    'severity'):
                        # must fetch sort values for that columns
                        # instead of comparing them as strings
                        with self.env.db_query as db:
                            for name, value in db(
                                    "SELECT name, %s FROM enum WHERE type=%%s"
                                    % db.cast('value', 'int'), (sort_col, )):
                                sort_values[name] = value

                    def sortkey(row):
                        val = row[idx]
                        # check if we have sort_values, then use them as keys.
                        if sort_values:
                            return sort_values.get(val)
                        # otherwise, continue with string comparison:
                        if isinstance(val, basestring):
                            val = val.lower()
                        return val

                    results = sorted(results, key=sortkey, reverse=not asc)

            header_group = header_groups[-1]

            if col.startswith('__') and col.endswith('__'):  # __col__
                header['hidden'] = True
            elif col[0] == '_' and col[-1] == '_':  # _col_
                header_group = []
                header_groups.append(header_group)
                header_groups.append([])
            elif col[0] == '_':  # _col
                header['hidden'] = True
            elif col[-1] == '_':  # col_
                header_groups.append([])
            header_group.append(header)

        # Structure the rows and cells:
        #  - group rows according to __group__ value, if defined
        #  - group cells the same way headers are grouped
        chrome = Chrome(self.env)
        row_groups = []
        authorized_results = []
        prev_group_value = None
        for row_idx, result in enumerate(results):
            col_idx = 0
            cell_groups = []
            row = {'cell_groups': cell_groups}
            realm = TicketSystem.realm
            parent_realm = ''
            parent_id = ''
            email_cells = []
            for header_group in header_groups:
                cell_group = []
                for header in header_group:
                    value = cell_value(result[col_idx])
                    cell = {'value': value, 'header': header, 'index': col_idx}
                    col = header['col']
                    col_idx += 1
                    # Detect and create new group
                    if col == '__group__' and value != prev_group_value:
                        prev_group_value = value
                        # Brute force handling of email in group by header
                        row_groups.append(
                            (value and chrome.format_author(req, value), []))
                    # Other row properties
                    row['__idx__'] = row_idx
                    if col in self._html_cols:
                        row[col] = value
                    if col in ('report', 'ticket', 'id', '_id'):
                        row['id'] = value
                    # Special casing based on column name
                    col = col.strip('_')
                    if col in ('reporter', 'cc', 'owner'):
                        email_cells.append(cell)
                    elif col == 'realm':
                        realm = value
                    elif col == 'parent_realm':
                        parent_realm = value
                    elif col == 'parent_id':
                        parent_id = value
                    cell_group.append(cell)
                cell_groups.append(cell_group)
            if parent_realm:
                resource = Resource(realm,
                                    row.get('id'),
                                    parent=Resource(parent_realm, parent_id))
            else:
                resource = Resource(realm, row.get('id'))
            # FIXME: for now, we still need to hardcode the realm in the action
            if resource.realm.upper() + '_VIEW' not in req.perm(resource):
                continue
            authorized_results.append(result)
            if email_cells:
                for cell in email_cells:
                    emails = chrome.format_emails(context.child(resource),
                                                  cell['value'])
                    result[cell['index']] = cell['value'] = emails
            row['resource'] = resource
            if row_groups:
                row_group = row_groups[-1][1]
            else:
                row_group = []
                row_groups = [(None, row_group)]
            row_group.append(row)

        data.update({
            'header_groups': header_groups,
            'row_groups': row_groups,
            'numrows': numrows
        })

        if format == 'rss':
            data['context'] = web_context(req, report_resource, absurls=True)
            return 'report.rss', data, 'application/rss+xml'
        elif format == 'csv':
            filename = 'report_%s.csv' % id if id else 'report.csv'
            self._send_csv(req,
                           cols,
                           authorized_results,
                           mimetype='text/csv',
                           filename=filename)
        elif format == 'tab':
            filename = 'report_%s.tsv' % id if id else 'report.tsv'
            self._send_csv(req,
                           cols,
                           authorized_results,
                           '\t',
                           mimetype='text/tab-separated-values',
                           filename=filename)
        else:
            p = page if max is not None else None
            add_link(req, 'alternate',
                     auth_link(req, report_href(format='rss', page=None)),
                     _('RSS Feed'), 'application/rss+xml', 'rss')
            add_link(req, 'alternate', report_href(format='csv', page=p),
                     _('Comma-delimited Text'), 'text/plain')
            add_link(req, 'alternate', report_href(format='tab', page=p),
                     _('Tab-delimited Text'), 'text/plain')
            if 'REPORT_SQL_VIEW' in req.perm(self.realm, id):
                add_link(req, 'alternate', req.href.report(id=id,
                                                           format='sql'),
                         _('SQL Query'), 'text/plain')

            # reuse the session vars of the query module so that
            # the query navigation links on the ticket can be used to
            # navigate report results as well
            try:
                req.session['query_tickets'] = \
                    ' '.join(str(int(row['id']))
                             for rg in row_groups for row in rg[1])
                req.session['query_href'] = \
                    req.session['query_href'] = report_href()
                # Kludge: we have to clear the other query session
                # variables, but only if the above succeeded
                for var in ('query_constraints', 'query_time'):
                    if var in req.session:
                        del req.session[var]
            except (ValueError, KeyError):
                pass
            if set(data['args']) - {'USER'}:
                data['show_args_form'] = True
                # Add values of all select-type ticket fields for autocomplete.
                fields = TicketSystem(self.env).get_ticket_fields()
                arg_values = {}
                for arg in set(data['args']) - {'USER'}:
                    attrs = fields.by_name(arg.lower())
                    if attrs and 'options' in attrs:
                        arg_values[attrs['name']] = attrs['options']
                if arg_values:
                    add_script_data(req, arg_values=arg_values)
                    Chrome(self.env).add_jquery_ui(req)
            if missing_args:
                add_warning(
                    req,
                    _('The following arguments are missing: %(args)s',
                      args=", ".join(missing_args)))
            return 'report_view.html', data, None
Пример #37
0
 def render():
     d = {'alist': attachments.copy()}
     d['compact'] = True
     d['foldable'] = True
     return Chrome(self.env).render_template(
         req, 'list_of_attachments.html', d, fragment=True)
Пример #38
0
 def _format_author(self, author):
     return Chrome(self.env).format_author(None, author)
Пример #39
0
 def _format_body(self, data, template_name):
     chrome = Chrome(self.env)
     template = chrome.load_template(template_name, text=True)
     with translation_deactivated():  # don't translate the e-mail stream
         body = chrome.render_template_string(template, data, text=True)
         return body.encode('utf-8')
Пример #40
0
    def render_ticket_action_control(self, req, ticket, action):

        self.log.debug('render_ticket_action_control: action "%s"', action)

        this_action = self.actions[action]
        status = this_action['newstate']
        operations = this_action['operations']
        current_owner = ticket._old.get('owner', ticket['owner'])
        author = get_reporter_id(req, 'author')
        author_info = partial(Chrome(self.env).authorinfo,
                              req,
                              resource=ticket.resource)
        formatted_current_owner = author_info(current_owner)
        exists = ticket._old.get('status', ticket['status']) is not None

        control = []  # default to nothing
        hints = []
        if 'reset_workflow' in operations:
            control.append(_("from invalid state"))
            hints.append(_("Current state no longer exists"))
        if 'del_owner' in operations:
            hints.append(_("The ticket will be disowned"))
        if 'set_owner' in operations or 'may_set_owner' in operations:
            if 'set_owner' in this_action:
                owners = self._to_users(this_action['set_owner'], ticket)
            elif self.config.getbool('ticket', 'restrict_owner'):
                perm = PermissionSystem(self.env)
                owners = perm.get_users_with_permission('TICKET_MODIFY')
                owners = [
                    user for user in owners
                    if 'TICKET_MODIFY' in PermissionCache(
                        self.env, user, ticket.resource)
                ]
                owners = sorted(owners)
            else:
                owners = None

            if 'set_owner' in operations:
                default_owner = author
            elif 'may_set_owner' in operations:
                if not exists:
                    default_owner = TicketSystem(self.env).default_owner
                else:
                    default_owner = ticket._old.get('owner', ticket['owner']
                                                    or None)
                if owners is not None and default_owner not in owners:
                    owners.insert(0, default_owner)
            else:
                # Protect against future modification for case that another
                # operation is added to the outer conditional
                raise AssertionError(operations)

            id = 'action_%s_reassign_owner' % action

            if not owners:
                owner = req.args.get(id, default_owner)
                control.append(
                    tag_("to %(owner)s",
                         owner=tag.input(type='text',
                                         id=id,
                                         name=id,
                                         value=owner)))
                if not exists or current_owner is None:
                    hints.append(_("The owner will be the specified user"))
                else:
                    hints.append(
                        tag_(
                            "The owner will be changed from "
                            "%(current_owner)s to the specified "
                            "user",
                            current_owner=formatted_current_owner))
            elif len(owners) == 1:
                owner = tag.input(type='hidden',
                                  id=id,
                                  name=id,
                                  value=owners[0])
                formatted_new_owner = author_info(owners[0])
                control.append(
                    tag_("to %(owner)s", owner=tag(formatted_new_owner,
                                                   owner)))
                if not exists or current_owner is None:
                    hints.append(
                        tag_("The owner will be %(new_owner)s",
                             new_owner=formatted_new_owner))
                elif ticket['owner'] != owners[0]:
                    hints.append(
                        tag_(
                            "The owner will be changed from "
                            "%(current_owner)s to %(new_owner)s",
                            current_owner=formatted_current_owner,
                            new_owner=formatted_new_owner))
            else:
                selected_owner = req.args.get(id, default_owner)
                control.append(
                    tag_("to %(owner)s",
                         owner=tag.select([
                             tag.option(x if x is not None else _("(none)"),
                                        value=x if x is not None else '',
                                        selected=(x == selected_owner or None))
                             for x in owners
                         ],
                                          id=id,
                                          name=id)))
                if not exists or current_owner is None:
                    hints.append(_("The owner will be the selected user"))
                else:
                    hints.append(
                        tag_(
                            "The owner will be changed from "
                            "%(current_owner)s to the selected user",
                            current_owner=formatted_current_owner))
        elif 'set_owner_to_self' in operations and \
                ticket._old.get('owner', ticket['owner']) != author:
            formatted_author = author_info(author)
            if not exists or current_owner is None:
                hints.append(
                    tag_("The owner will be %(new_owner)s",
                         new_owner=formatted_author))
            else:
                hints.append(
                    tag_(
                        "The owner will be changed from "
                        "%(current_owner)s to %(new_owner)s",
                        current_owner=formatted_current_owner,
                        new_owner=formatted_author))
        if 'set_resolution' in operations:
            if 'set_resolution' in this_action:
                resolutions = this_action['set_resolution']
            else:
                resolutions = [r.name for r in Resolution.select(self.env)]
            if not resolutions:
                raise TracError(
                    _("Your workflow attempts to set a resolution "
                      "but none is defined (configuration issue, "
                      "please contact your Trac admin)."))
            id = 'action_%s_resolve_resolution' % action
            if len(resolutions) == 1:
                resolution = tag.input(type='hidden',
                                       id=id,
                                       name=id,
                                       value=resolutions[0])
                control.append(
                    tag_("as %(resolution)s",
                         resolution=tag(resolutions[0], resolution)))
                hints.append(
                    tag_("The resolution will be set to %(name)s",
                         name=resolutions[0]))
            else:
                selected_option = req.args.get(
                    id,
                    TicketSystem(self.env).default_resolution)
                control.append(
                    tag_(
                        "as %(resolution)s",
                        resolution=tag.select([
                            tag.option(x,
                                       value=x,
                                       selected=(x == selected_option or None))
                            for x in resolutions
                        ],
                                              id=id,
                                              name=id)))
                hints.append(_("The resolution will be set"))
        if 'del_resolution' in operations:
            hints.append(_("The resolution will be deleted"))
        if 'leave_status' in operations:
            control.append(
                tag_("as %(status)s",
                     status=ticket._old.get('status', ticket['status'])))
            if len(operations) == 1:
                hints.append(
                    tag_("The owner will remain %(current_owner)s",
                         current_owner=formatted_current_owner) if
                    current_owner else _("The ticket will remain with no owner"
                                         ))
        else:
            if ticket['status'] is None:
                hints.append(tag_("The status will be '%(name)s'",
                                  name=status))
            elif status != '*':
                hints.append(
                    tag_("Next status will be '%(name)s'", name=status))
        return (this_action['label'], tag(separated(control, ' ')),
                tag(separated(hints, '. ', '.') if hints else ''))
Пример #41
0
    def _render_admin_panel(self, req, cat, page, component):
        # Detail view?
        if component:
            comp = model.Component(self.env, component)
            if req.method == 'POST':
                if req.args.get('save'):
                    comp.name = name = req.args.get('name')
                    comp.owner = req.args.get('owner')
                    comp.description = req.args.get('description')
                    try:
                        comp.update()
                    except self.env.db_exc.IntegrityError:
                        raise TracError(
                            _('The component "%(name)s" already '
                              'exists.',
                              name=name))
                    add_notice(req, _("Your changes have been saved."))
                    req.redirect(req.href.admin(cat, page))
                elif req.args.get('cancel'):
                    req.redirect(req.href.admin(cat, page))

            Chrome(self.env).add_wiki_toolbars(req)
            data = {'view': 'detail', 'component': comp}

        else:
            default = self.config.get('ticket', 'default_component')
            if req.method == 'POST':
                # Add Component
                if req.args.get('add') and req.args.get('name'):
                    name = req.args.get('name')
                    try:
                        comp = model.Component(self.env, name=name)
                    except ResourceNotFound:
                        comp = model.Component(self.env)
                        comp.name = name
                        if req.args.get('owner'):
                            comp.owner = req.args.get('owner')
                        comp.insert()
                        add_notice(
                            req,
                            _('The component "%(name)s" has been '
                              'added.',
                              name=name))
                        req.redirect(req.href.admin(cat, page))
                    else:
                        if comp.name is None:
                            raise TracError(_("Invalid component name."))
                        raise TracError(
                            _("Component %(name)s already exists.", name=name))

                # Remove components
                elif req.args.get('remove'):
                    sel = req.args.get('sel')
                    if not sel:
                        raise TracError(_("No component selected"))
                    if not isinstance(sel, list):
                        sel = [sel]
                    with self.env.db_transaction:
                        for name in sel:
                            model.Component(self.env, name).delete()
                    add_notice(
                        req, _("The selected components have been "
                               "removed."))
                    req.redirect(req.href.admin(cat, page))

                # Set default component
                elif req.args.get('apply'):
                    name = req.args.get('default')
                    if name and name != default:
                        self.log.info("Setting default component to %s", name)
                        self.config.set('ticket', 'default_component', name)
                        _save_config(self.config, req, self.log)
                        req.redirect(req.href.admin(cat, page))

                # Clear default component
                elif req.args.get('clear'):
                    self.log.info("Clearing default component")
                    self.config.set('ticket', 'default_component', '')
                    _save_config(self.config, req, self.log)
                    req.redirect(req.href.admin(cat, page))

            data = {
                'view': 'list',
                'components': list(model.Component.select(self.env)),
                'default': default
            }

        if self.config.getbool('ticket', 'restrict_owner'):
            perm = PermissionSystem(self.env)

            def valid_owner(username):
                return perm.get_user_permissions(username).get('TICKET_MODIFY')

            data['owners'] = [
                username
                for username, name, email in self.env.get_known_users()
                if valid_owner(username)
            ]
            data['owners'].insert(0, '')
            data['owners'].sort()
        else:
            data['owners'] = None

        return 'admin_components.html', data
Пример #42
0
    def render_admin_panel(self, req, category, page, path_info):
        req.perm.require('VERSIONCONTROL_ADMIN')

        # Retrieve info for all repositories
        rm = RepositoryManager(self.env)
        all_repos = rm.get_all_repositories()
        db_provider = self.env[DbRepositoryProvider]

        if path_info:
            # Detail view
            reponame = path_info if not is_default(path_info) else ''
            info = all_repos.get(reponame)
            if info is None:
                raise TracError(
                    _("Repository '%(repo)s' not found", repo=path_info))
            if req.method == 'POST':
                if req.args.get('cancel'):
                    req.redirect(req.href.admin(category, page))

                elif db_provider and req.args.get('save'):
                    # Modify repository
                    changes = {}
                    for field in db_provider.repository_attrs:
                        value = normalize_whitespace(req.args.get(field))
                        if (value is not None or field == 'hidden') \
                                and value != info.get(field):
                            changes[field] = value
                    if 'dir' in changes \
                            and not self._check_dir(req, changes['dir']):
                        changes = {}
                    if changes:
                        db_provider.modify_repository(reponame, changes)
                        add_notice(req, _('Your changes have been saved.'))
                    name = req.args.get('name')
                    resync = tag.tt('trac-admin $ENV repository resync "%s"' %
                                    (name or '(default)'))
                    if 'dir' in changes:
                        msg = tag_(
                            'You should now run %(resync)s to '
                            'synchronize Trac with the repository.',
                            resync=resync)
                        add_notice(req, msg)
                    elif 'type' in changes:
                        msg = tag_(
                            'You may have to run %(resync)s to '
                            'synchronize Trac with the repository.',
                            resync=resync)
                        add_notice(req, msg)
                    if name and name != path_info and not 'alias' in info:
                        cset_added = tag.tt('trac-admin $ENV changeset '
                                            'added "%s" $REV' %
                                            (name or '(default)'))
                        msg = tag_(
                            'You will need to update your post-commit '
                            'hook to call %(cset_added)s with the new '
                            'repository name.',
                            cset_added=cset_added)
                        add_notice(req, msg)
                    if changes:
                        req.redirect(req.href.admin(category, page))

            Chrome(self.env).add_wiki_toolbars(req)
            data = {'view': 'detail', 'reponame': reponame}

        else:
            # List view
            if req.method == 'POST':
                # Add a repository
                if db_provider and req.args.get('add_repos'):
                    name = req.args.get('name')
                    type_ = req.args.get('type')
                    # Avoid errors when copy/pasting paths
                    dir = normalize_whitespace(req.args.get('dir', ''))
                    if name is None or type_ is None or not dir:
                        add_warning(
                            req, _('Missing arguments to add a '
                                   'repository.'))
                    elif self._check_dir(req, dir):
                        db_provider.add_repository(name, dir, type_)
                        name = name or '(default)'
                        add_notice(
                            req,
                            _('The repository "%(name)s" has been '
                              'added.',
                              name=name))
                        resync = tag.tt('trac-admin $ENV repository resync '
                                        '"%s"' % name)
                        msg = tag_(
                            'You should now run %(resync)s to '
                            'synchronize Trac with the repository.',
                            resync=resync)
                        add_notice(req, msg)
                        cset_added = tag.tt('trac-admin $ENV changeset '
                                            'added "%s" $REV' % name)
                        msg = tag_(
                            'You should also set up a post-commit hook '
                            'on the repository to call %(cset_added)s '
                            'for each committed changeset.',
                            cset_added=cset_added)
                        add_notice(req, msg)
                        req.redirect(req.href.admin(category, page))

                # Add a repository alias
                elif db_provider and req.args.get('add_alias'):
                    name = req.args.get('name')
                    alias = req.args.get('alias')
                    if name is not None and alias is not None:
                        db_provider.add_alias(name, alias)
                        add_notice(
                            req,
                            _('The alias "%(name)s" has been '
                              'added.',
                              name=name or '(default)'))
                        req.redirect(req.href.admin(category, page))
                    add_warning(req, _('Missing arguments to add an '
                                       'alias.'))

                # Refresh the list of repositories
                elif req.args.get('refresh'):
                    req.redirect(req.href.admin(category, page))

                # Remove repositories
                elif db_provider and req.args.get('remove'):
                    sel = req.args.getlist('sel')
                    if sel:
                        for name in sel:
                            db_provider.remove_repository(name)
                        add_notice(
                            req,
                            _('The selected repositories have '
                              'been removed.'))
                        req.redirect(req.href.admin(category, page))
                    add_warning(req, _('No repositories were selected.'))

            data = {'view': 'list'}

        # Find repositories that are editable
        db_repos = {}
        if db_provider is not None:
            db_repos = dict(db_provider.get_repositories())

        # Prepare common rendering data
        repositories = dict(
            (reponame,
             self._extend_info(reponame, info.copy(), reponame in db_repos))
            for (reponame, info) in all_repos.iteritems())
        types = sorted([''] + rm.get_supported_types())
        data.update({
            'types': types,
            'default_type': rm.repository_type,
            'repositories': repositories
        })

        return 'admin_repositories.html', data
Пример #43
0
 def test_template_dirs_added(self):
     from trac.web.chrome import Chrome
     self.assertTrue(self.tag_wm in Chrome(self.env).template_providers)
Пример #44
0
    def _render_admin_panel(self, req, cat, page, milestone):
        req.perm.require('MILESTONE_VIEW')

        # Detail view?
        if milestone:
            mil = model.Milestone(self.env, milestone)
            if req.method == 'POST':
                if req.args.get('save'):
                    req.perm.require('MILESTONE_MODIFY')
                    mil.name = name = req.args.get('name')
                    mil.due = mil.completed = None
                    due = req.args.get('duedate', '')
                    if due:
                        mil.due = user_time(req,
                                            parse_date,
                                            due,
                                            hint='datetime')
                    if req.args.get('completed', False):
                        completed = req.args.get('completeddate', '')
                        mil.completed = user_time(req,
                                                  parse_date,
                                                  completed,
                                                  hint='datetime')
                        if mil.completed > datetime.now(utc):
                            raise TracError(
                                _('Completion date may not be in '
                                  'the future'), _('Invalid Completion Date'))
                    mil.description = req.args.get('description', '')
                    try:
                        mil.update()
                    except self.env.db_exc.IntegrityError:
                        raise TracError(
                            _('The milestone "%(name)s" already '
                              'exists.',
                              name=name))
                    add_notice(req, _('Your changes have been saved.'))
                    req.redirect(req.href.admin(cat, page))
                elif req.args.get('cancel'):
                    req.redirect(req.href.admin(cat, page))

            Chrome(self.env).add_wiki_toolbars(req)
            data = {'view': 'detail', 'milestone': mil}

        else:
            default = self.config.get('ticket', 'default_milestone')
            if req.method == 'POST':
                # Add Milestone
                if req.args.get('add') and req.args.get('name'):
                    req.perm.require('MILESTONE_CREATE')
                    name = req.args.get('name')
                    try:
                        mil = model.Milestone(self.env, name=name)
                    except ResourceNotFound:
                        mil = model.Milestone(self.env)
                        mil.name = name
                        if req.args.get('duedate'):
                            mil.due = user_time(req,
                                                parse_date,
                                                req.args.get('duedate'),
                                                hint='datetime')
                        mil.insert()
                        add_notice(
                            req,
                            _('The milestone "%(name)s" has been '
                              'added.',
                              name=name))
                        req.redirect(req.href.admin(cat, page))
                    else:
                        if mil.name is None:
                            raise TracError(_('Invalid milestone name.'))
                        raise TracError(
                            _("Milestone %(name)s already exists.", name=name))

                # Remove milestone
                elif req.args.get('remove'):
                    req.perm.require('MILESTONE_DELETE')
                    sel = req.args.get('sel')
                    if not sel:
                        raise TracError(_('No milestone selected'))
                    if not isinstance(sel, list):
                        sel = [sel]
                    with self.env.db_transaction:
                        for name in sel:
                            mil = model.Milestone(self.env, name)
                            mil.delete(author=req.authname)
                    add_notice(
                        req, _("The selected milestones have been "
                               "removed."))
                    req.redirect(req.href.admin(cat, page))

                # Set default milestone
                elif req.args.get('apply'):
                    name = req.args.get('default')
                    if name and name != default:
                        self.log.info("Setting default milestone to %s", name)
                        self.config.set('ticket', 'default_milestone', name)
                        _save_config(self.config, req, self.log)
                        req.redirect(req.href.admin(cat, page))

            # Get ticket count
            milestones = [(milestone,
                           self.env.db_query(
                               """
                    SELECT COUNT(*) FROM ticket WHERE milestone=%s
                    """, (milestone.name, ))[0][0])
                          for milestone in model.Milestone.select(self.env)]

            data = {
                'view': 'list',
                'milestones': milestones,
                'default': default
            }

        data.update({
            'datetime_hint': get_datetime_format_hint(req.lc_time),
        })
        return 'admin_milestones.html', data
Пример #45
0
 def _format_body(self, data, template_name):
     template = Chrome(self.env).load_template(template_name, method='text')
     stream = template.generate(**data)
     # don't translate the e-mail stream
     with translation_deactivated():
         return stream.render('text', encoding='utf-8')
Пример #46
0
    def _render_admin_panel(self, req, cat, page, version):
        # Detail view?
        if version:
            ver = model.Version(self.env, version)
            if req.method == 'POST':
                if req.args.get('save'):
                    ver.name = name = req.args.get('name')
                    if req.args.get('time'):
                        ver.time = user_time(req,
                                             parse_date,
                                             req.args.get('time'),
                                             hint='datetime')
                    else:
                        ver.time = None  # unset
                    ver.description = req.args.get('description')
                    try:
                        ver.update()
                    except self.env.db_exc.IntegrityError:
                        raise TracError(
                            _('The version "%(name)s" already '
                              'exists.',
                              name=name))

                    add_notice(req, _('Your changes have been saved.'))
                    req.redirect(req.href.admin(cat, page))
                elif req.args.get('cancel'):
                    req.redirect(req.href.admin(cat, page))

            Chrome(self.env).add_wiki_toolbars(req)
            data = {'view': 'detail', 'version': ver}

        else:
            default = self.config.get('ticket', 'default_version')
            if req.method == 'POST':
                # Add Version
                if req.args.get('add') and req.args.get('name'):
                    name = req.args.get('name')
                    try:
                        ver = model.Version(self.env, name=name)
                    except ResourceNotFound:
                        ver = model.Version(self.env)
                        ver.name = name
                        if req.args.get('time'):
                            ver.time = user_time(req,
                                                 parse_date,
                                                 req.args.get('time'),
                                                 hint='datetime')
                        ver.insert()
                        add_notice(
                            req,
                            _('The version "%(name)s" has been '
                              'added.',
                              name=name))
                        req.redirect(req.href.admin(cat, page))
                    else:
                        if ver.name is None:
                            raise TracError(_("Invalid version name."))
                        raise TracError(
                            _("Version %(name)s already exists.", name=name))

                # Remove versions
                elif req.args.get('remove'):
                    sel = req.args.get('sel')
                    if not sel:
                        raise TracError(_("No version selected"))
                    if not isinstance(sel, list):
                        sel = [sel]
                    with self.env.db_transaction:
                        for name in sel:
                            ver = model.Version(self.env, name)
                            ver.delete()
                    add_notice(
                        req, _("The selected versions have been "
                               "removed."))
                    req.redirect(req.href.admin(cat, page))

                # Set default version
                elif req.args.get('apply'):
                    name = req.args.get('default')
                    if name and name != default:
                        self.log.info("Setting default version to %s", name)
                        self.config.set('ticket', 'default_version', name)
                        _save_config(self.config, req, self.log)
                        req.redirect(req.href.admin(cat, page))

            data = {
                'view': 'list',
                'versions': model.Version.select(self.env),
                'default': default
            }

        data.update({
            'datetime_hint': get_datetime_format_hint(req.lc_time),
        })
        return 'admin_versions.html', data
Пример #47
0
    def _render_admin_panel(self, req, cat, page, milestone):
        perm = req.perm('admin', 'ticket/' + self._type)
        # Detail view?
        if milestone:
            mil = model.Milestone(self.env, milestone)
            if req.method == 'POST':
                if req.args.get('save'):
                    perm.require('MILESTONE_MODIFY')
                    mil.name = name = req.args.get('name')
                    mil.due = mil.completed = None
                    if 'due' in req.args:
                        duedate = req.args.get('duedate')
                        mil.due = user_time(req, parse_date, duedate,
                                            hint='datetime') \
                                  if duedate else None
                    if req.args.get('completed', False):
                        completed = req.args.get('completeddate', '')
                        mil.completed = user_time(req,
                                                  parse_date,
                                                  completed,
                                                  hint='datetime')
                        if mil.completed > datetime.now(utc):
                            raise TracError(
                                _("Completion date may not be in "
                                  "the future"), _("Invalid Completion Date"))
                    mil.description = req.args.get('description', '')
                    try:
                        mil.update(author=req.authname)
                    except self.env.db_exc.IntegrityError:
                        raise TracError(
                            _('The milestone "%(name)s" already '
                              'exists.',
                              name=name))
                    add_notice(req, _('Your changes have been saved.'))
                    req.redirect(req.href.admin(cat, page))
                elif req.args.get('cancel'):
                    req.redirect(req.href.admin(cat, page))

            now = datetime.now(req.tz)
            default_due = datetime(now.year, now.month, now.day, 18)
            if now.hour > 18:
                default_due += timedelta(days=1)
            default_due = to_datetime(default_due, req.tz)
            Chrome(self.env).add_wiki_toolbars(req)
            data = {
                'view': 'detail',
                'milestone': mil,
                'default_due': default_due
            }

        else:
            ticket_default = self.config.get('ticket', 'default_milestone')
            retarget_default = self.config.get('milestone',
                                               'default_retarget_to')
            if req.method == 'POST':
                # Add Milestone
                if req.args.get('add') and req.args.get('name'):
                    perm.require('MILESTONE_CREATE')
                    name = req.args.get('name')
                    try:
                        mil = model.Milestone(self.env, name=name)
                    except ResourceNotFound:
                        mil = model.Milestone(self.env)
                        mil.name = name
                        if req.args.get('duedate'):
                            mil.due = user_time(req,
                                                parse_date,
                                                req.args.get('duedate'),
                                                hint='datetime')
                        mil.insert()
                        add_notice(
                            req,
                            _('The milestone "%(name)s" has been '
                              'added.',
                              name=name))
                        req.redirect(req.href.admin(cat, page))
                    else:
                        if mil.name is None:
                            raise TracError(_('Invalid milestone name.'))
                        raise TracError(
                            _("Milestone %(name)s already exists.", name=name))

                # Remove milestone
                elif req.args.get('remove'):
                    perm.require('MILESTONE_DELETE')
                    sel = req.args.get('sel')
                    if not sel:
                        raise TracError(_('No milestone selected'))
                    if not isinstance(sel, list):
                        sel = [sel]
                    with self.env.db_transaction:
                        for name in sel:
                            mil = model.Milestone(self.env, name)
                            mil.delete(author=req.authname)
                    add_notice(
                        req, _("The selected milestones have been "
                               "removed."))
                    req.redirect(req.href.admin(cat, page))

                # Set default milestone
                elif req.args.get('apply'):
                    save = False
                    name = req.args.get('ticket_default')
                    if name and name != ticket_default:
                        self.log.info(
                            "Setting default ticket "
                            "milestone to %s", name)
                        self.config.set('ticket', 'default_milestone', name)
                        save = True
                    retarget = req.args.get('retarget_default')
                    if retarget and retarget != retarget_default:
                        self.log.info(
                            "Setting default retargeting "
                            "milestone to %s", retarget)
                        self.config.set('milestone', 'default_retarget_to',
                                        retarget)
                        save = True
                    if save:
                        _save_config(self.config, req, self.log)
                        req.redirect(req.href.admin(cat, page))

                # Clear defaults
                elif req.args.get('clear'):
                    self.log.info("Clearing default ticket milestone "
                                  "and default retarget milestone")
                    self.config.set('ticket', 'default_milestone', '')
                    self.config.set('milestone', 'default_retarget_to', '')
                    _save_config(self.config, req, self.log)
                    req.redirect(req.href.admin(cat, page))

            # Get ticket count
            milestones = [(milestone,
                           self.env.db_query(
                               """
                    SELECT COUNT(*) FROM ticket WHERE milestone=%s
                    """, (milestone.name, ))[0][0])
                          for milestone in model.Milestone.select(self.env)]

            query_href = lambda name: req.href.query({
                'groupby': 'status',
                'milestone': name
            })

            data = {
                'view': 'list',
                'milestones': milestones,
                'query_href': query_href,
                'ticket_default': ticket_default,
                'retarget_default': retarget_default
            }

        Chrome(self.env).add_jquery_ui(req)

        data.update({
            'datetime_hint': get_datetime_format_hint(req.lc_time),
        })
        return 'admin_milestones.html', data
Пример #48
0
    def _render_editor(self, req, page, action='edit', has_collision=False):
        if has_collision:
            if action == 'merge':
                page = WikiPage(self.env, page.name, version=None)
                req.perm(page.resource).require('WIKI_VIEW')
            else:
                action = 'collision'

        if page.readonly:
            req.perm(page.resource).require('WIKI_ADMIN')
        else:
            req.perm(page.resource).require('WIKI_MODIFY')
        original_text = page.text
        comment = req.args.get('comment', '')
        if 'text' in req.args:
            page.text = req.args.get('text')
        elif 'template' in req.args:
            template = self.PAGE_TEMPLATES_PREFIX + req.args.get('template')
            template_page = WikiPage(self.env, template)
            if template_page and template_page.exists and \
                   'WIKI_VIEW' in req.perm(template_page.resource):
                page.text = template_page.text
        elif 'version' in req.args:
            old_page = WikiPage(self.env,
                                page.name,
                                version=int(req.args['version']))
            req.perm(page.resource).require('WIKI_VIEW')
            page.text = old_page.text
            comment = _("Reverted to version %(version)s.",
                        version=req.args['version'])
        if action in ('preview', 'diff'):
            page.readonly = 'readonly' in req.args

        author = get_reporter_id(req, 'author')
        defaults = {'editrows': 20}
        prefs = dict((key, req.session.get('wiki_%s' % key, defaults.get(key)))
                     for key in ('editrows', 'sidebyside'))

        if 'from_editor' in req.args:
            sidebyside = req.args.get('sidebyside') or None
            if sidebyside != prefs['sidebyside']:
                req.session.set('wiki_sidebyside', int(bool(sidebyside)), 0)
        else:
            sidebyside = prefs['sidebyside']

        if sidebyside:
            editrows = max(int(prefs['editrows']),
                           len(page.text.splitlines()) + 1)
        else:
            editrows = req.args.get('editrows')
            if editrows:
                if editrows != prefs['editrows']:
                    req.session.set('wiki_editrows', editrows,
                                    defaults['editrows'])
            else:
                editrows = prefs['editrows']

        data = self._page_data(req, page, action)
        context = web_context(req, page.resource)
        data.update({
            'author':
            author,
            'comment':
            comment,
            'edit_rows':
            editrows,
            'sidebyside':
            sidebyside,
            'scroll_bar_pos':
            req.args.get('scroll_bar_pos', ''),
            'diff':
            None,
            'attachments':
            AttachmentModule(self.env).attachment_data(context),
        })
        if action in ('diff', 'merge'):
            old_text = original_text.splitlines() if original_text else []
            new_text = page.text.splitlines() if page.text else []
            diff_data, changes = self._prepare_diff(req, page, old_text,
                                                    new_text, page.version, '')
            data.update({
                'diff': diff_data,
                'changes': changes,
                'action': 'preview',
                'merge': action == 'merge',
                'longcol': 'Version',
                'shortcol': 'v'
            })
        elif sidebyside and action != 'collision':
            data['action'] = 'preview'

        self._wiki_ctxtnav(req, page)
        Chrome(self.env).add_wiki_toolbars(req)
        Chrome(self.env).add_auto_preview(req)
        add_script(req, 'common/js/folding.js')
        return 'wiki_edit.html', data, None
Пример #49
0
Файл: git_fs.py Проект: t2y/trac
    def render_property(self, name, mode, context, props):
        def sha_link(sha, label=None):
            # sha is assumed to be a non-abbreviated 40-chars sha id
            try:
                reponame = context.resource.parent.id
                repos = self.env.get_repository(reponame)
                cset = repos.get_changeset(sha)
                if label is None:
                    label = repos.display_rev(sha)

                return tag.a(label,
                             class_='changeset',
                             title=shorten_line(cset.message),
                             href=context.href.changeset(sha, repos.reponame))

            except Exception as e:
                return tag.a(sha,
                             class_='missing changeset',
                             title=to_unicode(e),
                             rel='nofollow')

        if name == 'Branches':
            branches = props[name]

            # simple non-merge commit
            return tag(*intersperse(', ', (sha_link(rev, label)
                                           for label, rev in branches)))

        elif name in ('Parents', 'Children'):
            revs = props[name]  # list of commit ids

            if name == 'Parents' and len(revs) > 1:
                # we got a merge...
                current_sha = context.resource.id
                reponame = context.resource.parent.id

                parent_links = intersperse(', ', \
                    ((sha_link(rev),
                      ' (',
                      tag.a('diff',
                            title="Diff against this parent (show the " \
                                  "changes merged from the other parents)",
                            href=context.href.changeset(current_sha, reponame,
                                                        old=rev)),
                      ')')
                     for rev in revs))

                return tag(
                    list(parent_links), tag.br(),
                    tag.span(tag(
                        "Note: this is a ", tag.strong("merge"), " changeset, "
                        "the changes displayed below "
                        "correspond to the merge itself."),
                             class_='hint'), tag.br(),
                    tag.span(tag(
                        "Use the ", tag.code("(diff)"),
                        " links above to see all the changes "
                        "relative to each parent."),
                             class_='hint'))

            # simple non-merge commit
            return tag(*intersperse(', ', map(sha_link, revs)))

        elif name in ('git-committer', 'git-author'):
            user_, time_ = props[name]
            _str = "%s (%s)" % (Chrome(self.env).format_author(
                context.req,
                user_), format_datetime(time_, tzinfo=context.req.tz))
            return unicode(_str)

        raise TracError("Internal error")
Пример #50
0
    def dispatch(self, req):
        """Find a registered handler that matches the request and let
        it process it.
        
        In addition, this method initializes the data dictionary
        passed to the the template and adds the web site chrome.
        """
        self.log.debug('Dispatching %r', req)
        chrome = Chrome(self.env)

        # Setup request callbacks for lazily-evaluated properties
        req.callbacks.update({
            'authname': self.authenticate,
            'chrome': chrome.prepare_request,
            'perm': self._get_perm,
            'session': self._get_session,
            'locale': self._get_locale,
            'lc_time': self._get_lc_time,
            'tz': self._get_timezone,
            'form_token': self._get_form_token
        })

        try:
            try:
                # Select the component that should handle the request
                chosen_handler = None
                try:
                    for handler in self.handlers:
                        if handler.match_request(req):
                            chosen_handler = handler
                            break
                    if not chosen_handler:
                        if not req.path_info or req.path_info == '/':
                            chosen_handler = self.default_handler
                    # pre-process any incoming request, whether a handler
                    # was found or not
                    chosen_handler = self._pre_process_request(
                        req, chosen_handler)
                except TracError, e:
                    raise HTTPInternalError(e)
                if not chosen_handler:
                    if req.path_info.endswith('/'):
                        # Strip trailing / and redirect
                        target = req.path_info.rstrip('/').encode('utf-8')
                        if req.query_string:
                            target += '?' + req.query_string
                        req.redirect(req.href + target, permanent=True)
                    raise HTTPNotFound('No handler matched request to %s',
                                       req.path_info)

                req.callbacks['chrome'] = partial(chrome.prepare_request,
                                                  handler=chosen_handler)

                # Protect against CSRF attacks: we validate the form token
                # for all POST requests with a content-type corresponding
                # to form submissions
                if req.method == 'POST':
                    ctype = req.get_header('Content-Type')
                    if ctype:
                        ctype, options = cgi.parse_header(ctype)
                    if ctype in ('application/x-www-form-urlencoded',
                                 'multipart/form-data') and \
                            req.args.get('__FORM_TOKEN') != req.form_token:
                        if self.env.secure_cookies and req.scheme == 'http':
                            msg = _('Secure cookies are enabled, you must '
                                    'use https to submit forms.')
                        else:
                            msg = _('Do you have cookies enabled?')
                        raise HTTPBadRequest(
                            _('Missing or invalid form token.'
                              ' %(msg)s',
                              msg=msg))

                # Process the request and render the template
                resp = chosen_handler.process_request(req)
                if resp:
                    if len(resp) == 2:  # old Clearsilver template and HDF data
                        self.log.error(
                            "Clearsilver template are no longer "
                            "supported (%s)", resp[0])
                        raise TracError(
                            _("Clearsilver templates are no longer supported, "
                              "please contact your Trac administrator."))
                    # Genshi
                    template, data, content_type = \
                              self._post_process_request(req, *resp)
                    if 'hdfdump' in req.args:
                        req.perm.require('TRAC_ADMIN')
                        # debugging helper - no need to render first
                        out = StringIO()
                        pprint(data, out)
                        req.send(out.getvalue(), 'text/plain')

                    output = chrome.render_template(req, template, data,
                                                    content_type)
                    req.send(output, content_type or 'text/html')
                else:
                    self._post_process_request(req)
            except RequestDone:
                # Give the session a chance to persist changes after a send()
                req.session.save()
                raise
            except:
                # post-process the request in case of errors
                err = sys.exc_info()
                try:
                    self._post_process_request(req)
                except RequestDone:
                    raise
                except Exception, e:
                    self.log.error(
                        "Exception caught while post-processing"
                        " request: %s", exception_to_unicode(e,
                                                             traceback=True))
                raise err[0], err[1], err[2]
Пример #51
0
 def __init__(self):
     self.has_pretty_dateinfo = hasattr(Chrome(self.env),
                                        'default_dateinfo_format')
Пример #52
0
class SearchModuleTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()
        self.search_module = SearchModule(self.env)
        self.chrome = Chrome(self.env)
        pages_dir = pkg_resources.resource_filename('trac.wiki',
                                                    'default-pages')
        for page_name in ('WikiStart', 'TracModWSGI'):
            page = os.path.join(pages_dir, page_name)
            WikiAdmin(self.env).import_page(page, page_name)

    def tearDown(self):
        self.env.reset_db()

    def _insert_ticket(self, **kw):
        """Helper for inserting a ticket into the database"""
        return insert_ticket(self.env, **kw)

    def _process_request(self, req):
        self.assertEqual(True, self.search_module.match_request(req))
        return self.search_module.process_request(req)

    def _render_template(self, req, template, data):
        rendered = self.chrome.render_template(req, template, data, {
            'iterable': False,
            'fragment': False
        })
        return rendered.decode('utf-8')

    def test_process_request_page_in_range(self):
        for _ in xrange(21):
            self._insert_ticket(summary="Trac")
        req = MockRequest(self.env,
                          path_info='/search',
                          args={
                              'page': '3',
                              'q': 'Trac',
                              'ticket': 'on'
                          })

        data = self._process_request(req)[1]

        self.assertEqual([], req.chrome['warnings'])
        self.assertEqual(2, data['results'].page)

    def test_process_request_page_out_of_range(self):
        """Out of range value for page defaults to page 1."""
        for _ in xrange(20):
            self._insert_ticket(summary="Trac")
        req = MockRequest(self.env,
                          path_info='/search',
                          args={
                              'page': '3',
                              'q': 'Trac',
                              'ticket': 'on'
                          })

        data = self._process_request(req)[1]

        self.assertIn("Page 3 is out of range.", req.chrome['warnings'])
        self.assertEqual(0, data['results'].page)

    def test_camelcase_quickjump(self):
        """CamelCase word does quick-jump."""
        req = MockRequest(self.env,
                          path_info='/search',
                          args={'q': 'WikiStart'})

        self.assertRaises(RequestDone, self._process_request, req)

        self.assertEqual('http://example.org/trac.cgi/wiki/WikiStart',
                         req.headers_sent['Location'])
        self.assertIn("You arrived here through", req.chrome['notices'][0])
        self.assertIn(
            '<a href="/trac.cgi/search?'
            'q=WikiStart&amp;noquickjump=1">here</a>',
            req.chrome['notices'][0])

    def test_non_camelcase_no_quickjump(self):
        """Non-CamelCase word does not quick-jump."""
        req = MockRequest(self.env,
                          path_info='/search',
                          args={'q': 'TracModWSGI'})

        data = self._process_request(req)[1]

        results = list(data['results'])
        self.assertIsNone(data['quickjump'])
        self.assertEqual('TracModWSGI', data['query'])
        self.assertEqual(1, len(results))
        self.assertEqual('/trac.cgi/wiki/TracModWSGI', results[0]['href'])
        self.assertEqual([], req.chrome['notices'])

    def test_rendering_noquickjump_unicode_error(self):
        """Test for regression of https://trac.edgewall.org/ticket/13212
        """
        def do_render(query):
            req = MockRequest(self.env,
                              path_info='/search',
                              args={
                                  'q': query,
                                  'noquickjump': '1'
                              })
            template, data = self._process_request(req)
            return self._render_template(req, template, data)

        self.assertIn(
            u'<a href="/trac.cgi/query?id=1-2">Quickjump to <em>'
            u'ticket:1,\u200b2</em></a>', do_render('ticket:1,2'))
        self.assertIn(
            u'<a href="mailto:[email protected]">Quickjump to <em>'
            u'<span class="icon">\u200b</span>[email protected]'
            u'</em></a>', do_render('*****@*****.**'))
Пример #53
0
    def expand_macro(self, formatter, name, content, realms=[]):
        """Evaluate macro call and render results.

        Calls from web-UI come with pre-processed realm selection.
        """
        env = self.env
        req = formatter.req
        tag_system = TagSystem(env)

        all_realms = set([p.get_taggable_realm()
                         for p in tag_system.tag_providers])
        if not all_realms:
            # Tag providers are required, no result without at least one.
            return ''
        args, kw = parse_args(content)

        query = args and args[0].strip() or None
        if not realms:
            # Check macro arguments for realms (typical wiki macro call).
            realms = 'realm' in kw and kw['realm'].split('|') or []
        if query:
            # Add realms from query expression.
            realms.extend(query_realms(query, all_realms))
            # Remove redundant realm selection for performance.
            if set(realms) == all_realms:
                query = re.sub('(^|\W)realm:\S+(\W|$)', ' ', query).strip()
        if name == 'TagCloud':
            # Set implicit 'all tagged realms' as default.
            if not realms:
                realms = all_realms
            if query:
                all_tags = Counter()
                # Require per resource query including view permission checks.
                for resource, tags in tag_system.query(req, query):
                    all_tags.update(tags)
            else:
                # Allow faster per tag query, side steps permission checks.
                all_tags = tag_system.get_all_tags(req, realms=realms)
            mincount = 'mincount' in kw and kw['mincount'] or None
            return self.render_cloud(req, all_tags,
                                     caseless_sort=self.caseless_sort,
                                     mincount=mincount, realms=realms)
        elif name == 'ListTagged':
            if content and _OBSOLETE_ARGS_RE.search(content):
                data = {'warning': 'obsolete_args'}
            else:
                data = {'warning': None}
            context = formatter.context
            # Use TagsQuery arguments (most likely wiki macro calls).
            cols = 'cols' in kw and kw['cols'] or self.default_cols
            format = 'format' in kw and kw['format'] or self.default_format
            if not realms:
                # Apply ListTagged defaults to macro call w/o realm.
                realms = list(set(all_realms)-set(self.exclude_realms))
                if not realms:
                    return ''
            query = '(%s) (%s)' % (query or '', ' or '.join(['realm:%s' % (r)
                                                             for r in realms]))
            env.log.debug('LISTTAGGED_QUERY: %s', query)
            query_result = tag_system.query(req, query)
            if not query_result:
                return ''

            def _link(resource):
                if resource.realm == 'tag':
                    # Keep realm selection in tag links.
                    return builder.a(resource.id,
                                     href=self.get_href(req, realms,
                                                        tag=resource))
                elif resource.realm == 'ticket':
                    # Return resource link including ticket status dependend
                    #   class to allow for common Trac ticket link style.
                    ticket = Ticket(env, resource.id)
                    return builder.a('#%s' % ticket.id,
                                     class_=ticket['status'],
                                     href=formatter.href.ticket(ticket.id),
                                     title=shorten_line(ticket['summary']))
                return render_resource_link(env, context, resource, 'compact')

            if format == 'table':
                cols = [col for col in cols.split('|')
                        if col in self.supported_cols]
                # Use available translations from Trac core.
                try:
                    labels = TicketSystem(env).get_ticket_field_labels()
                    labels['id'] = _('Id')
                except AttributeError:
                    # Trac 0.11 neither has the attribute nor uses i18n.
                    labels = {'id': 'Id', 'description': 'Description'}
                labels['realm'] = _('Realm')
                labels['tags'] = _('Tags')
                headers = [{'label': labels.get(col)}
                           for col in cols]
                data.update({'cols': cols,
                             'headers': headers})

            results = sorted(query_result, key=lambda r: \
                             embedded_numbers(to_unicode(r[0].id)))
            results = self._paginate(req, results, realms)
            rows = []
            for resource, tags in results:
                desc = tag_system.describe_tagged_resource(req, resource)
                # Fallback to resource provider method.
                desc = desc or get_resource_description(env, resource,
                                                        context=context)
                tags = sorted(tags)
                wiki_desc = format_to_oneliner(env, context, desc)
                if tags:
                    rendered_tags = [_link(Resource('tag', tag))
                                     for tag in tags]
                    if 'oldlist' == format:
                        resource_link = _link(resource)
                    else:
                        resource_link = builder.a(wiki_desc,
                                                  href=get_resource_url(
                                                  env, resource, context.href))
                        if 'table' == format:
                            cells = []
                            for col in cols:
                                if col == 'id':
                                    cells.append(_link(resource))
                                # Don't duplicate links to resource in both.
                                elif col == 'description' and 'id' in cols:
                                    cells.append(wiki_desc)
                                elif col == 'description':
                                    cells.append(resource_link)
                                elif col == 'realm':
                                    cells.append(resource.realm)
                                elif col == 'tags':
                                    cells.append(
                                        builder([(tag, ' ')
                                                 for tag in rendered_tags]))
                            rows.append({'cells': cells})
                            continue
                rows.append({'desc': wiki_desc,
                             'rendered_tags': None,
                             'resource_link': _link(resource)})
            data.update({'format': format,
                         'paginator': results,
                         'results': rows,
                         'tags_url': req.href('tags')})

            # Work around a bug in trac/templates/layout.html, that causes a
            # TypeError for the wiki macro call, if we use add_link() alone.
            add_stylesheet(req, 'common/css/search.css')

            return Chrome(env).render_template(
                req, 'listtagged_results.html', data, 'text/html', True)
Пример #54
0
    def render_preference_panel(self, req, panel, path_info=None):
        if req.method == 'POST':
            action_arg = req.args.get('action', '')
            m = re.match('^([^_]+)_(.+)', action_arg)
            if m:
                action, arg = m.groups()
                handler = self.post_handlers.get(action)
                if handler:
                    handler(arg, req)
                    add_notice(req, _("Your preferences have been saved."))
            req.redirect(req.href.prefs('notification'))

        data = {'rules': {}, 'subscribers': []}

        desc_map = {}
        defaults = []

        data['formatters'] = {}
        data['selected_format'] = {}
        data['adverbs'] = ('always', 'never')
        data['adverb_labels'] = dict(always=_("Notify"),
                                     never=_("Never notify"))

        for i in self.subscribers:
            if not i.description():
                continue
            if not req.session.authenticated and i.requires_authentication():
                continue
            data['subscribers'].append({
                'class': i.__class__.__name__,
                'description': i.description()
            })
            desc_map[i.__class__.__name__] = i.description()
            if hasattr(i, 'default_subscriptions'):
                defaults.extend(i.default_subscriptions())

        for t in self._iter_transports():
            data['rules'][t] = []
            styles = self._get_supported_styles(t)
            data['formatters'][t] = styles
            data['selected_format'][t] = styles[0]
            for r in Subscription.find_by_sid_and_distributor(
                    self.env, req.session.sid, req.session.authenticated, t):
                if desc_map.get(r['class']):
                    data['rules'][t].append({
                        'id': r['id'],
                        'adverb': r['adverb'],
                        'class': r['class'],
                        'description': desc_map[r['class']],
                        'priority': r['priority']
                    })
                    data['selected_format'][t] = r['format']

        data['default_rules'] = {}
        for r in sorted(defaults, key=itemgetter(3)):
            klass, dist, format, priority, adverb = r
            if not data['default_rules'].get(dist):
                data['default_rules'][dist] = []
            if desc_map.get(klass):
                data['default_rules'][dist].append({
                    'adverb':
                    adverb,
                    'description':
                    desc_map.get(klass)
                })

        Chrome(self.env).add_jquery_ui(req)
        return 'prefs_notification.html', dict(data=data)
    def render_ticket_action_control(self, req, ticket, action):

        self.log.debug('render_ticket_action_control: action "%s"' % action)

        this_action = self.actions[action]
        status = this_action['newstate']
        operations = this_action['operations']
        current_owner = ticket._old.get('owner', ticket['owner'] or '(none)')
        if not (Chrome(self.env).show_email_addresses
                or 'EMAIL_VIEW' in req.perm(ticket.resource)):
            format_user = obfuscate_email_address
        else:
            format_user = lambda address: address
        current_owner = format_user(current_owner)

        control = []  # default to nothing
        hints = []
        if 'reset_workflow' in operations:
            control.append(tag("from invalid state "))
            hints.append(_("Current state no longer exists"))
        if 'del_owner' in operations:
            hints.append(_("The ticket will be disowned"))
        if 'set_owner' in operations:
            id = 'action_%s_reassign_owner' % action
            selected_owner = req.args.get(id, req.authname)

            if this_action.has_key('set_owner'):
                owners = [
                    x.strip() for x in this_action['set_owner'].split(',')
                ]
            elif self.config.getbool('ticket', 'restrict_owner'):
                perm = PermissionSystem(self.env)
                owners = perm.get_users_with_permission('TICKET_MODIFY')
                owners.sort()
            else:
                owners = None

            if owners == None:
                owner = req.args.get(id, req.authname)
                control.append(
                    tag_('to %(owner)s',
                         owner=tag.input(type='text',
                                         id=id,
                                         name=id,
                                         value=owner)))
                hints.append(
                    _("The owner will be changed from "
                      "%(current_owner)s",
                      current_owner=current_owner))
            elif len(owners) == 1:
                owner = tag.input(type='hidden',
                                  id=id,
                                  name=id,
                                  value=owners[0])
                formatted_owner = format_user(owners[0])
                control.append(
                    tag_('to %(owner)s ', owner=tag(formatted_owner, owner)))
                if ticket['owner'] != owners[0]:
                    hints.append(
                        _(
                            "The owner will be changed from "
                            "%(current_owner)s to %(selected_owner)s",
                            current_owner=current_owner,
                            selected_owner=formatted_owner))
            else:
                control.append(
                    tag_('to %(owner)s',
                         owner=tag.select([
                             tag.option(x,
                                        value=x,
                                        selected=(x == selected_owner or None))
                             for x in owners
                         ],
                                          id=id,
                                          name=id)))
                hints.append(
                    _("The owner will be changed from "
                      "%(current_owner)s",
                      current_owner=current_owner))
        if 'set_owner_to_self' in operations and \
                ticket._old.get('owner', ticket['owner']) != req.authname:
            hints.append(
                _(
                    "The owner will be changed from %(current_owner)s "
                    "to %(authname)s",
                    current_owner=current_owner,
                    authname=req.authname))
        if 'set_resolution' in operations:
            if this_action.has_key('set_resolution'):
                resolutions = [
                    x.strip() for x in this_action['set_resolution'].split(',')
                ]
            else:
                resolutions = [val.name for val in Resolution.select(self.env)]
            if not resolutions:
                raise TracError(
                    _("Your workflow attempts to set a resolution "
                      "but none is defined (configuration issue, "
                      "please contact your Trac admin)."))
            id = 'action_%s_resolve_resolution' % action
            if len(resolutions) == 1:
                resolution = tag.input(type='hidden',
                                       id=id,
                                       name=id,
                                       value=resolutions[0])
                control.append(
                    tag_('as %(resolution)s',
                         resolution=tag(resolutions[0], resolution)))
                hints.append(
                    _("The resolution will be set to %(name)s",
                      name=resolutions[0]))
            else:
                selected_option = req.args.get(
                    id,
                    TicketSystem(self.env).default_resolution)
                control.append(
                    tag_(
                        'as %(resolution)s',
                        resolution=tag.select([
                            tag.option(x,
                                       value=x,
                                       selected=(x == selected_option or None))
                            for x in resolutions
                        ],
                                              id=id,
                                              name=id)))
                hints.append(_("The resolution will be set"))
        if 'del_resolution' in operations:
            hints.append(_("The resolution will be deleted"))
        if 'leave_status' in operations:
            control.append(
                _('as %(status)s ',
                  status=ticket._old.get('status', ticket['status'])))
        else:
            if status != '*':
                hints.append(_("Next status will be '%(name)s'", name=status))
        return (this_action['name'], tag(*control), '. '.join(hints))
Пример #56
0
 def _render_template(self, req):
     data = {'keywords': self.keywords}
     return Chrome(self.env). \
         render_template(req, 'keywords.html', data, 'MarkupTemplate', True)
Пример #57
0
    def process_request(self, req):
        """ Processing the request. """

        req.perm('blog').assert_permission('BLOG_VIEW')

        blog_core = FullBlogCore(self.env)
        format = req.args.get('format', '').lower()

        command, pagename, path_items, listing_data = self._parse_path(req)
        action = req.args.get('action', 'view').lower()
        version = req.args.getint('version', 0)

        data = {}
        template = 'fullblog_view.html'
        data['blog_about'] = BlogPost(self.env, 'about')
        data['blog_infotext'] = blog_core.get_bloginfotext()
        blog_month_names = map_month_names(
                    self.env.config.getlist('fullblog', 'month_names'))
        data['blog_month_names'] = blog_month_names
        self.log.debug("Blog debug: command=%r, pagename=%r, path_items=%r",
                       command, pagename, path_items)

        if not command:
            # Request for just root (display latest)
            data['blog_post_list'] = []
            count = 0
            maxcount = self.num_items
            blog_posts = get_blog_posts(self.env)
            for post in blog_posts:
                bp = BlogPost(self.env, post[0], post[1])
                if 'BLOG_VIEW' in req.perm(bp.resource):
                    data['blog_post_list'].append(bp)
                    count += 1
                if maxcount and count == maxcount:
                    # Only display a certain number on front page (from config)
                    break
            data['blog_list_title'] = "Recent posts" + \
                    (len(blog_posts) > maxcount and \
                        " (max %d) - Browse or Archive for more" % (maxcount,) \
                    or '')
            add_link(req, 'alternate', req.href.blog(format='rss'), 'RSS Feed',
                     'application/rss+xml', 'rss')

        elif command == 'archive':
            # Requesting the archive page
            template = 'fullblog_archive.html'
            data['blog_archive'] = []
            for period, period_posts in group_posts_by_month(get_blog_posts(self.env)):
                allowed_posts = []
                for post in period_posts:
                    bp = BlogPost(self.env, post[0], post[1])
                    if 'BLOG_VIEW' in req.perm(bp.resource):
                        allowed_posts.append(post)
                if allowed_posts:
                    data['blog_archive'].append((period, allowed_posts))
            add_link(req, 'alternate', req.href.blog(format='rss'), 'RSS Feed',
                     'application/rss+xml', 'rss')

        elif command == 'view' and pagename:
            # Requesting a specific blog post
            the_post = BlogPost(self.env, pagename, version)
            req.perm(the_post.resource).require('BLOG_VIEW')
            if not the_post.version:
                raise HTTPNotFound("No blog post named '%s'." % pagename)
            if req.method == 'POST':   # Adding/Previewing a comment
                # Permission?
                req.perm(the_post.resource).require('BLOG_COMMENT')
                comment = BlogComment(self.env, pagename)
                comment.comment = req.args.get('comment', '')
                comment.author = (req.authname != 'anonymous' and req.authname) \
                            or req.args.get('author')
                comment.time = datetime.datetime.now(utc)
                warnings = []
                if 'cancelcomment' in req.args:
                    req.redirect(req.href.blog(pagename))
                elif 'previewcomment' in req.args:
                    data['comment_preview'] = True
                    warnings.extend(blog_core.create_comment(req, comment, verify_only=True))
                elif 'submitcomment' in req.args and not warnings:
                    warnings.extend(blog_core.create_comment(req, comment))
                    if not warnings:
                        req.redirect(req.href.blog(pagename
                                )+'#comment-'+str(comment.number))
                data['blog_comment'] = comment
                # Push all warnings out to the user.
                for field, reason in warnings:
                    if field:
                        add_warning(req, "Field '%s': %s" % (field, reason))
                    else:
                        add_warning(req, reason)
            data['blog_post'] = the_post
            context = web_context(req, the_post.resource,
                            absurls=format=='rss' and True or False)
            data['context'] = context
            if format == 'rss':
                return 'fullblog_post.rss', data, 'application/rss+xml'
            # Regular web response
            context = web_context(req, the_post.resource)

            data['blog_attachments'] = AttachmentModule(self.env).attachment_data(context)
            # Previous and Next ctxtnav
            prev, next = blog_core.get_prev_next_posts(req.perm, the_post.name)
            if prev:
                add_link(req, 'prev', req.href.blog(prev), prev)
            if next:
                add_link(req, 'next', req.href.blog(next), next)
            if arity(prevnext_nav) == 4:
                # 0.12 compat following trac:changeset:8597
                prevnext_nav(req, 'Previous Post', 'Next Post')
            else:
                prevnext_nav(req, 'Post')
            # RSS feed for post and comments
            add_link(req, 'alternate', req.href.blog(pagename, format='rss'),
                            'RSS Feed', 'application/rss+xml', 'rss')

        elif command in ['create', 'edit']:
            template = 'fullblog_edit.html'
            default_pagename = blog_core._get_default_postname(req.authname)
            the_post = BlogPost(self.env, pagename or default_pagename)
            warnings = []

            if command == 'create' and req.method == 'GET' and not the_post.version:
                # Support appending query arguments for populating intial fields
                the_post.update_fields(req.args)
            if command == 'create' and the_post.version:
                # Post with name or suggested name already exists
                if 'BLOG_CREATE' in req.perm and the_post.name == default_pagename \
                                    and not req.method == 'POST':
                    if default_pagename:
                        add_notice(req, "Suggestion for new name already exists "
                            "('%s'). Please make a new name." % the_post.name)
                elif pagename:
                    warnings.append(
                        ('', "A post named '%s' already exists. Enter new name."
                                            % the_post.name))
                the_post = BlogPost(self.env, '')
            if command == 'edit':
                req.perm(the_post.resource).require('BLOG_VIEW') # Starting point
            if req.method == 'POST':
                # Create or edit a blog post
                if 'blog-cancel' in req.args:
                    if req.args.get('action','') == 'edit':
                        req.redirect(req.href.blog(pagename))
                    else:
                        req.redirect(req.href.blog())
                # Assert permissions
                if command == 'create':
                    req.perm(Resource('blog', None)).require('BLOG_CREATE')
                elif command == 'edit':
                    if the_post.author == req.authname:
                        req.perm(the_post.resource).require('BLOG_MODIFY_OWN')
                    else:
                        req.perm(the_post.resource).require('BLOG_MODIFY_ALL')

                # Check input
                orig_author = the_post.author
                if not the_post.update_fields(req.args):
                    warnings.append(('', "None of the fields have changed."))
                version_comment = req.args.get('new_version_comment', '')
                if 'blog-preview' in req.args:
                    warnings.extend(blog_core.create_post(
                            req, the_post, req.authname, version_comment, verify_only=True))
                elif 'blog-save' in req.args and not warnings:
                    warnings.extend(blog_core.create_post(
                            req, the_post, req.authname, version_comment))
                    if not warnings:
                        req.redirect(req.href.blog(the_post.name))
                context = web_context(req, the_post.resource)
                data['context'] = context
                data['blog_attachments'] = AttachmentModule(self.env).attachment_data(context)
                data['blog_action'] = 'preview'
                data['blog_version_comment'] = version_comment
                if (orig_author and orig_author != the_post.author) and (
                        not 'BLOG_MODIFY_ALL' in req.perm(the_post.resource)):
                    add_notice(req, "If you change the author you cannot " \
                        "edit the post again due to restricted permissions.")
                    data['blog_orig_author'] = orig_author
            for field, reason in warnings:
                if field:
                    add_warning(req, "Field '%s': %s" % (field, reason))
                else:
                    add_warning(req, reason)
            data['blog_edit'] = the_post

        elif command == 'delete':
            bp = BlogPost(self.env, pagename)
            req.perm(bp.resource).require('BLOG_DELETE')
            if 'blog-cancel' in req.args:
                req.redirect(req.href.blog(pagename))
            comment = req.args.getint('comment', 0)
            warnings = []
            if comment:
                # Deleting a specific comment
                bc = BlogComment(self.env, pagename, comment)
                if not bc.number:
                    raise TracError(
                            "Cannot delete. Blog post name and/or comment number missing.")
                if req.method == 'POST' and comment and pagename:
                    warnings.extend(blog_core.delete_comment(bc))
                    if not warnings:
                        add_notice(req, "Blog comment %d deleted." % comment)
                        req.redirect(req.href.blog(pagename))
                template = 'fullblog_delete.html'
                data['blog_comment'] = bc
            else:
                # Delete a version of a blog post or all versions
                # with comments and attachments if only version.
                if not bp.version:
                    raise TracError(
                            "Cannot delete. Blog post '%s' does not exist." % (
                                    bp.name))
                version = req.args.getint('version', 0)
                if req.method == 'POST':
                    if 'blog-version-delete' in req.args:
                        if bp.version != version:
                            raise TracError(
                                "Cannot delete. Can only delete most recent version.")
                        warnings.extend(blog_core.delete_post(bp, version=bp.versions[-1]))
                    elif 'blog-delete' in req.args:
                        version = 0
                        warnings.extend(blog_core.delete_post(bp, version=version))
                    if not warnings:
                        if version > 1:
                            add_notice(req, "Blog post '%s' version %d deleted." % (
                                                pagename, version))
                            req.redirect(req.href.blog(pagename))
                        else:
                            add_notice(req, "Blog post '%s' deleted." % pagename)
                            req.redirect(req.href.blog())
                template = 'fullblog_delete.html'
                data['blog_post'] = bp
            for field, reason in warnings:
                if field:
                    add_warning(req, "Field '%s': %s" % (field, reason))
                else:
                    add_warning(req, reason)

        elif command.startswith('listing-'):
            # 2007/10 or category/something or author/theuser
            title = category = author = ''
            from_dt = to_dt = None
            if command == 'listing-month':
                from_dt = listing_data['from_dt']
                to_dt = listing_data['to_dt']
                title = "Posts for the month of %s %d" % (
                        blog_month_names[from_dt.month -1], from_dt.year)
                add_link(req, 'alternate', req.href.blog(format='rss'), 'RSS Feed',
                        'application/rss+xml', 'rss')

            elif command == 'listing-category':
                category = listing_data['category']
                if category:
                    title = "Posts in category %s" % category
                    add_link(req, 'alternate', req.href.blog('category', category,
                        format='rss'), 'RSS Feed', 'application/rss+xml', 'rss')
            elif command == 'listing-author':
                author = listing_data['author']
                if author:
                    title = "Posts by author %s" % author
                    add_link(req, 'alternate', req.href.blog('author', author,
                        format='rss'), 'RSS Feed', 'application/rss+xml', 'rss')
            if not (author or category or (from_dt and to_dt)):
                raise HTTPNotFound("Not a valid path for viewing blog posts.")
            blog_posts = []
            for post in get_blog_posts(self.env, category=category,
                        author=author, from_dt=from_dt, to_dt=to_dt):
                bp = BlogPost(self.env, post[0], post[1])
                if 'BLOG_VIEW' in req.perm(bp.resource):
                    blog_posts.append(bp)
            data['blog_post_list'] = blog_posts
            data['blog_list_title'] = title
        else:
            raise HTTPNotFound("Not a valid blog path.")

        if (not command or command.startswith('listing-')) and format == 'rss':
            data['context'] = web_context(req, absurls=True)
            data['blog_num_items'] = self.num_items
            return 'fullblog.rss', data, 'application/rss+xml'

        data['blog_months'], data['blog_authors'], data['blog_categories'], \
                data['blog_total'] = \
                    blog_core.get_months_authors_categories(
                        user=req.authname, perm=req.perm)
        if 'BLOG_CREATE' in req.perm('blog'):
            add_ctxtnav(req, 'New Post', href=req.href.blog('create'),
                    title="Create new Blog Post")

        chrome = Chrome(self.env)
        chrome.add_auto_preview(req)
        chrome.add_wiki_toolbars(req)
        add_stylesheet(req, 'tracfullblog/css/fullblog.css')
        add_stylesheet(req, 'common/css/code.css')
        data['blog_personal_blog'] = self.env.config.getbool('fullblog',
                                                'personal_blog')
        data['blog_archive_rss_icon'] = self.all_rss_icons \
                                        or self.archive_rss_icon
        data['blog_all_rss_icons'] = self.all_rss_icons
        return template, data, {}
Пример #58
0
    def _format_plaintext(self, event):
        """Format ticket change notification e-mail (untranslated)"""
        ticket = event.target
        newticket = event.category == 'created'
        with translation_deactivated(ticket):
            link = self.env.abs_href.ticket(ticket.id)

            changes_body = ''
            changes_descr = ''
            change_data = {}
            if not newticket and event.time:  # Ticket change
                from trac.ticket.web_ui import TicketModule
                for change in TicketModule(self.env) \
                              .grouped_changelog_entries(ticket,
                                                         when=event.time):
                    if not change['permanent']:  # attachment with same time...
                        continue
                    author = change['author']
                    change_data.update({
                        'author':
                        self._format_author(author),
                        'comment':
                        wrap(change['comment'], self.COLS, ' ', ' ', '\n',
                             self.ambiwidth)
                    })
                    link += '#comment:%s' % str(change.get('cnum', ''))
                    for field, values in change['fields'].iteritems():
                        old = values['old']
                        new = values['new']
                        newv = ''
                        if field == 'description':
                            new_descr = wrap(new, self.COLS, ' ', ' ', '\n',
                                             self.ambiwidth)
                            old_descr = wrap(old, self.COLS, '> ', '> ', '\n',
                                             self.ambiwidth)
                            old_descr = old_descr.replace(
                                2 * '\n', '\n' + '>' + '\n')
                            cdescr = '\n'
                            cdescr += 'Old description:' + 2 * '\n' + old_descr + \
                                      2 * '\n'
                            cdescr += 'New description:' + 2 * '\n' + new_descr + \
                                      '\n'
                            changes_descr = cdescr
                        elif field == 'cc':
                            addcc, delcc = self._diff_cc(old, new)
                            chgcc = ''
                            if delcc:
                                chgcc += wrap(
                                    " * cc: %s (removed)" % ', '.join(delcc),
                                    self.COLS, ' ', ' ', '\n',
                                    self.ambiwidth) + '\n'
                            if addcc:
                                chgcc += wrap(
                                    " * cc: %s (added)" % ', '.join(addcc),
                                    self.COLS, ' ', ' ', '\n',
                                    self.ambiwidth) + '\n'
                            if chgcc:
                                changes_body += chgcc
                        else:
                            if field in ['owner', 'reporter']:
                                old = self._format_author(old)
                                new = self._format_author(new)
                            elif field in ticket.time_fields:
                                format = ticket.fields.by_name(field) \
                                                      .get('format')
                                old = self._format_time_field(old, format)
                                new = self._format_time_field(new, format)
                            newv = new
                            length = 7 + len(field)
                            spacer_old, spacer_new = ' ', ' '
                            if len(old + new) + length > self.COLS:
                                length = 5
                                if len(old) + length > self.COLS:
                                    spacer_old = '\n'
                                if len(new) + length > self.COLS:
                                    spacer_new = '\n'
                            chg = '* %s: %s%s%s=>%s%s' \
                                  % (field, spacer_old, old,
                                     spacer_old, spacer_new, new)
                            chg = chg.replace('\n', '\n' + length * ' ')
                            chg = wrap(chg, self.COLS, '', length * ' ', '\n',
                                       self.ambiwidth)
                            changes_body += ' %s%s' % (chg, '\n')
                        if newv:
                            change_data[field] = {
                                'oldvalue': old,
                                'newvalue': new
                            }

            ticket_values = ticket.values.copy()
            ticket_values['id'] = ticket.id
            ticket_values['description'] = wrap(ticket_values.get(
                'description', ''),
                                                self.COLS,
                                                initial_indent=' ',
                                                subsequent_indent=' ',
                                                linesep='\n',
                                                ambiwidth=self.ambiwidth)
            ticket_values['new'] = newticket
            ticket_values['link'] = link

            data = Chrome(self.env).populate_data(
                None, {
                    'CRLF': CRLF,
                    'ticket_props': self._format_props(ticket),
                    'ticket_body_hdr': self._format_hdr(ticket),
                    'ticket': ticket_values,
                    'changes_body': changes_body,
                    'changes_descr': changes_descr,
                    'change': change_data
                })
            return self._format_body(data, 'ticket_notify_email.txt')
Пример #59
0
            if col.startswith('__') and col.endswith('__'): # __col__
                header['hidden'] = True
            elif col[0] == '_' and col[-1] == '_':          # _col_
                header_group = []
                header_groups.append(header_group)
                header_groups.append([])
            elif col[0] == '_':                             # _col
                header['hidden'] = True
            elif col[-1] == '_':                            # col_
                header_groups.append([])
            header_group.append(header)

        # Structure the rows and cells:
        #  - group rows according to __group__ value, if defined
        #  - group cells the same way headers are grouped
        chrome = Chrome(self.env)
        row_groups = []
        authorized_results = []
        prev_group_value = None
        for row_idx, result in enumerate(results):
            col_idx = 0
            cell_groups = []
            row = {'cell_groups': cell_groups}
            realm = 'ticket'
            parent_realm = ''
            parent_id = ''
            email_cells = []
            for header_group in header_groups:
                cell_group = []
                for header in header_group:
                    value = cell_value(result[col_idx])
Пример #60
0
    def test_format_emails(self):
        format_emails = Chrome(self.env).format_emails
        to_format = '[email protected], user2; [email protected]'

        self.assertEqual(u'user1@\u2026, user2, user3@\u2026',
                         format_emails(None, to_format))