Esempio n. 1
0
    def _show_regular_page(self, req):
        req_data = {}
        # Resolve some static data needed on page (categories and project count)
        cs = CQDECategoryStore()
        all_categories = cs.get_all_categories()

        all_contexts = cs.get_contexts()
        combined_context_ids, combined_contexts, non_combined_contexts = self.get_combined_contexts(
            all_contexts)

        # Total count of projects and count of projects in categories
        direct_category_counts = Projects().get_project_counts_per_category(
            req.authname)

        req_data['direct_category_counts'] = direct_category_counts
        req_data['combined_context_ids'] = combined_context_ids
        req_data['non_combined_contexts'] = non_combined_contexts

        self.set_category_data_into_request_data(all_categories, req_data)

        add_stylesheet(req, 'multiproject/css/multiproject.css')
        add_script(req, 'multiproject/js/jquery.ba-bbq.js')
        add_script(req, 'multiproject/js/explore.js')

        return "find.html", {
            'top_categories': req_data['top_categories'],
            'categories': req_data['root_categories_of_combined_contexts'],
            'nonbrowsable_cntxt': req_data['non_empty_non_combined_contexts'],
            'selected_tab': self.DEFAULT_TAB,
            'tabs': self.TABS}, \
            None
Esempio n. 2
0
    def _show_regular_page(self, req):
        req_data = {}
        # Resolve some static data needed on page (categories and project count)
        cs = CQDECategoryStore()
        all_categories = cs.get_all_categories()

        all_contexts = cs.get_contexts()
        combined_context_ids, combined_contexts, non_combined_contexts = self.get_combined_contexts(all_contexts)

        # Total count of projects and count of projects in categories
        direct_category_counts = Projects().get_project_counts_per_category(req.authname)

        req_data['direct_category_counts'] = direct_category_counts
        req_data['combined_context_ids'] = combined_context_ids
        req_data['non_combined_contexts'] = non_combined_contexts

        self.set_category_data_into_request_data(all_categories, req_data)

        add_stylesheet(req, 'multiproject/css/multiproject.css')
        add_script(req, 'multiproject/js/jquery.ba-bbq.js')
        add_script(req, 'multiproject/js/explore.js')

        return "find.html", {
            'top_categories': req_data['top_categories'],
            'categories': req_data['root_categories_of_combined_contexts'],
            'nonbrowsable_cntxt': req_data['non_empty_non_combined_contexts'],
            'selected_tab': self.DEFAULT_TAB,
            'tabs': self.TABS}, \
            None
Esempio n. 3
0
    def _get_project_categories(self, project):
        """
        Create list of categories. License, Language and Development status are shown separately
        and are therefore taken out from the category listing.
        """
        # Get api
        cs = CQDECategoryStore()

        # Get categories and specialcase contexts
        categories = cs.get_all_project_categories(project.id)
        contexts = cs.get_contexts()

        combined_categories = []
        separated_categories_per_context = {}

        categories_by_any_context_id = {}
        context_by_id = {}

        # Setup the contexts dicts
        for context in contexts:
            context_by_id[context.context_id] = context
            if context.summary_name:
                # If context has summary name, it is shown as separated in the summary page
                new_list = []
                categories_by_any_context_id[context.context_id] = new_list
                separated_categories_per_context[context.context_id] = new_list
            else:
                categories_by_any_context_id[context.context_id] = combined_categories

        sort_opts = {'key':lambda c:c.name, 'cmp':lambda x,y: cmp(x.lower(), y.lower())}

        # Now, categories_by_any_context_id has key for each context id
        for category in categories:
            categories_by_any_context_id[category.context].append(category)
        for context_id in separated_categories_per_context:
            separated_categories_per_context[context_id].sort(**sort_opts)

        combined_categories.sort(**sort_opts)

        # Sort alphabetically, case-insensitively
        context_order = [c.context_id for c in
                         sorted(map(lambda id:context_by_id[id],
                             separated_categories_per_context.keys()),
                             key=lambda c:c.summary_name)]

        languages = []
        for context_id in separated_categories_per_context:
            context = context_by_id[context_id]
            if (context.summary_name
                and context.summary_name.lower() in ('language','natural language')):
                # Here, we expect that the description contains the language tag.
                # http://www.w3schools.com/tags/att_meta_http_equiv.asp
                # http://www.w3.org/TR/2011/WD-html-markup-20110113/meta.http-equiv.content-language.html
                languages = [c.description for c in separated_categories_per_context[context_id]]


        return (combined_categories, separated_categories_per_context, context_by_id,
               context_order, languages)
Esempio n. 4
0
    def getProjectCategories(self, req):
        """ Returns projects categorizations
        """
        categorylist = []

        store = CQDECategoryStore()

        for context in store.get_contexts():
            contextcategories = []
            categories = store.get_categories(context.context_id)
            for category in categories:
                contextcategories.append(category.name)
            categorylist.append(context.name)
            categorylist.append(contextcategories)

        return categorylist
Esempio n. 5
0
    def getProjectCategories(self, req):
        """ Returns projects categorizations
        """
        categorylist = []

        store = CQDECategoryStore()

        for context in store.get_contexts():
            contextcategories = []
            categories = store.get_categories(context.context_id)
            for category in categories:
                contextcategories.append(category.name)
            categorylist.append(context.name)
            categorylist.append(contextcategories)

        return categorylist
Esempio n. 6
0
class CategorizationAdminPanel(Component):
    """ Trac admin panel component for categorizing project
    """
    implements(IAdminPanelProvider, IRequestHandler)

    def __init__(self):
        self.categ = CQDECategoryStore()
        self.first_context = None

    # IAdminPanelProvider interface requirement
    def get_admin_panels(self, req):
        """ Admin panel navigation items
        """
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', 'General', 'categorization', 'Categorization')

    # IAdminPanelProvider interface requirement
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders categorization admin panel
        """
        req.perm.require('TRAC_ADMIN')

        context_data = self._get_context_data()
        if req.args.get('add_and_bind'):
            self.add_and_bind_category(req, context_data)

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')

        all_contexts = context_data['all_contexts']
        not_separate_context_ids = context_data['not_separate_context_ids']
        context_by_id = context_data['context_by_id']
        combined_context_ids = context_data['combined_context_ids']

        # First index is reserved for main context
        contexts = [None]
        main_context_id = context_data['main_context_id']
        for context in all_contexts:
            if context.context_id in not_separate_context_ids:
                continue
            contexts.append(context)
        if main_context_id != -1:
            contexts[0] = context_by_id[main_context_id]
        else:
            # No main context id -> drop the first one
            contexts = contexts[1:]

        main_addable_contexts = []
        for context_id in not_separate_context_ids:
            if context_by_id[context_id].edit_type == Context.EDIT_TYPE_ADD:
                main_addable_contexts.append(context_by_id[context_id])

        cats_per_context, project_cats_per_context = self._get_categories_data(
            context_data)

        data = {}
        data['env'] = req.base_path
        data['contexts'] = contexts
        data['categories_per_context'] = cats_per_context
        data['project_categories_per_context'] = project_cats_per_context
        data['main_addable_contexts'] = main_addable_contexts
        data['not_separate_context_ids'] = not_separate_context_ids
        return 'admin_categories.html', data

    def match_request(self, req):
        """ Path to categorization for ajax requests
        """
        return re.match(r'/categories(?:_trac)?(?:/.*)?$', req.path_info)

    def process_request(self, req):
        """ Ajax request processing. Binding and unbinding categories
        """
        req.perm.require('TRAC_ADMIN')

        # Get parameters
        action = req.args.get('action')
        context_key = req.args.get('context_key')
        category_key = req.args.get('category_key')

        context_data = self._get_context_data(context_key)
        project_key = context_data['project_key']

        # Select action
        if "bind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.bind_category_project(project_key, category_key)
        elif "unbind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.unbind_category_project(project_key, category_key)

        return self.show_categories(req, context_key, context_data)

    def show_categories(self, req, context_key, context_data):
        """ Shows those categories that belongs into project and context
            that is given.
        """
        context_key = safe_int(context_key)

        if (context_key in context_data['combined_context_ids']
                or context_key in context_data['autocomplete_context_ids']):
            raise TracError('Cannot show combined categories')
        context_id = context_key
        # categories for main context are stored to index -1
        if context_key == context_data['main_context_id']:
            context_id = -1

        cats_per_context, project_cats_per_context = self._get_categories_data(
            context_data)
        data = {
            'context': context_data['context_by_id'][context_key],
            'categories': cats_per_context[context_id],
            'project_categories': project_cats_per_context[context_id],
            'env': req.base_path
        }

        return 'categories.html', data, None

    def add_and_bind_category(self, req, context_data):
        """
        Called by render_admin_panel
        """
        req.perm.require('TRAC_ADMIN')
        if req.method != 'POST':
            raise TracError('POST request required')

        category_name = req.args.get('category_name', '')
        l_url = req.args.get('license_url')
        if category_name and l_url:
            category_name = category_name.strip()
            l_url = l_url.strip()
            category_name = category_name + "#" + l_url

        cat = self.categ.get_category_by_name(category_name)
        if not cat:
            context_id = safe_int(req.args.get('context_id', '').strip())

            if not context_id or context_id not in context_data[
                    'context_by_id']:
                add_warning(req, _("Invalid context"))
                return

            if not category_name:
                add_warning(req, _("Invalid category"))
                return

            context = context_data['context_by_id'][context_id]

            if context.edit_type != context.EDIT_TYPE_ADD:
                add_warning(
                    req,
                    _("Adding category into context %(context_name) is not allowed",
                      context_name=context.name))
                return

            try:
                self.categ.add_category(category_name, category_name,
                                        context_id, None)
                cat = self.categ.get_category_by_name(category_name)
            except Exception as e:
                add_warning(
                    req,
                    _('Category %(what)s cannot be added.', what=category_name)
                    + _(str(e)))
                return

        project_key = context_data['project_key']
        if cat and project_key and self.categ.bind_category_project(
                project_key, cat.category_id):
            add_notice(
                req,
                _('Category %(what)s has been added.',
                  what=category_name.split("#")[0]))
        else:
            add_warning(
                req,
                _('Category %(what)s cannot be added.',
                  what=category_name.split("#")[0]))

    def _get_context_data(self, context_id=None):
        """

        Returns dict with keys and values (type means here context.admin_type)::
            combined_context_ids: ids of contexts of type combined but not autocomplete
            not_separate_context_ids: set of ids of contexts of type combined, main, or autocomplete
            autocomplete_context_ids: ids of contexts of type autocomplete
            main_context_id: id of the context with type main, defaults to -1
            context_by_id: context_id => context mapping
            all_contexts: all contexts
            involved_context_ids: ids of contexts for which the categories are needed to be fetched
            context_id: safe context id of the request

        :returns: dict with data
        """
        main_context_id = -1

        combined_context_ids = []
        autocomplete_context_ids = []
        not_separate_context_ids = set([])
        context_by_id = {}
        all_contexts = self.categ.get_contexts()
        context_id = safe_int(context_id)
        project_key = Project.get(self.env).id

        for context in all_contexts:
            context_by_id[context.context_id] = context
            if context.admin_type == context.ADMIN_TYPE_MAIN:
                if main_context_id != -1:
                    self.log.warning(
                        "Invalid category data: Duplicate main contexts")
                    context.admin_type = context.ADMIN_TYPE_COMBINED
                else:
                    main_context_id = context.context_id
                    not_separate_context_ids.add(context.context_id)
            if context.admin_type == context.ADMIN_TYPE_COMBINED:
                combined_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                autocomplete_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
        if main_context_id == -1:
            self.log.warning('Invalid category data: Main context not set!')

        involved_context_ids = []
        if context_id:
            if context_id not in context_by_id:
                raise TracError('Invalid context id provided %s' %
                                context_id.__class__)
            if context_id == main_context_id:
                involved_context_ids = [
                    main_context_id
                ] + combined_context_ids + autocomplete_context_ids
            else:
                involved_context_ids = [context_id]
        else:
            involved_context_ids = [
                context.context_id for context in all_contexts
            ]
        data = {
            'project_key': project_key,
            'autocomplete_context_ids': autocomplete_context_ids,
            'combined_context_ids': combined_context_ids,
            'not_separate_context_ids': not_separate_context_ids,
            'main_context_id': main_context_id,
            'context_by_id': context_by_id,
            'all_contexts': all_contexts,
            'involved_context_ids': involved_context_ids,
            'context_id': context_id
        }
        return data

    def _get_categories_data(self, context_data):
        """
        Returns 'categories_per_context' and 'project_categories_per_context',
        both being dictionaries with context_id keys and values, which are
        dictionaries with category_id keys and category values.
        """
        context_by_id = context_data['context_by_id']
        main_context_id = context_data['main_context_id']
        project_key = context_data['project_key']
        categories_per_context = {-1: {}}
        project_categories_per_context = {-1: {}}
        for i_context_id in context_data['involved_context_ids']:
            context = context_by_id[i_context_id]
            if context.admin_type in (context.ADMIN_TYPE_MAIN,
                                      context.ADMIN_TYPE_COMBINED):
                categories_per_context[-1].update(
                    self.categ.get_all_categories_in_context(i_context_id))
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id))
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                # categories_per_context[i_context_id] -- don't show anywhere!
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id))
            else:
                categories_per_context[i_context_id] = \
                    self.categ.get_all_categories_in_context(i_context_id)
                project_categories_per_context[i_context_id] = \
                    self._get_categories_by_project(project_key, i_context_id)
        categories_per_context[main_context_id] = categories_per_context[-1]
        project_categories_per_context[
            main_context_id] = project_categories_per_context[-1]

        return categories_per_context, project_categories_per_context

    def _get_categories_by_project(self, project_key, context_id):
        category_dict = {}
        for category in self.categ.get_categories_by_project(
                project_key, context_id):
            category_dict[category.category_id] = category
        return category_dict

    def _get_context_name(self, context_key, all_contexts=None):
        if not all_contexts:
            all_contexts = self.categ.get_contexts()

        for c in all_contexts:
            if c.context_id == int(context_key):
                return c.name
        return ""
Esempio n. 7
0
class CategorizationAdminPanel(Component):
    """ Trac admin panel component for categorizing project
    """
    implements(IAdminPanelProvider, IRequestHandler)

    def __init__(self):
        self.categ = CQDECategoryStore()
        self.first_context = None

    # IAdminPanelProvider interface requirement
    def get_admin_panels(self, req):
        """ Admin panel navigation items
        """
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', 'General', 'categorization', 'Categorization')

    # IAdminPanelProvider interface requirement
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders categorization admin panel
        """
        req.perm.require('TRAC_ADMIN')


        context_data = self._get_context_data()
        if req.args.get('add_and_bind'):
            self.add_and_bind_category(req, context_data)

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')

        all_contexts = context_data['all_contexts']
        not_separate_context_ids = context_data['not_separate_context_ids']
        context_by_id = context_data['context_by_id']
        combined_context_ids = context_data['combined_context_ids']

        # First index is reserved for main context
        contexts = [None]
        main_context_id = context_data['main_context_id']
        for context in all_contexts:
            if context.context_id in not_separate_context_ids:
                continue
            contexts.append(context)
        if main_context_id != -1:
            contexts[0] = context_by_id[main_context_id]
        else:
            # No main context id -> drop the first one
            contexts = contexts[1:]

        main_addable_contexts = []
        for context_id in not_separate_context_ids:
            if context_by_id[context_id].edit_type == Context.EDIT_TYPE_ADD:
                main_addable_contexts.append(context_by_id[context_id])

        cats_per_context, project_cats_per_context = self._get_categories_data(context_data)

        data = {}
        data['env'] = req.base_path
        data['contexts'] = contexts
        data['categories_per_context'] = cats_per_context
        data['project_categories_per_context'] = project_cats_per_context
        data['main_addable_contexts'] = main_addable_contexts
        data['not_separate_context_ids'] = not_separate_context_ids
        return 'admin_categories.html', data

    def match_request(self, req):
        """ Path to categorization for ajax requests
        """
        return re.match(r'/categories(?:_trac)?(?:/.*)?$', req.path_info)

    def process_request(self, req):
        """ Ajax request processing. Binding and unbinding categories
        """
        req.perm.require('TRAC_ADMIN')


        # Get parameters
        action = req.args.get('action')
        context_key = req.args.get('context_key')
        category_key = req.args.get('category_key')

        context_data = self._get_context_data(context_key)
        project_key = context_data['project_key']

        # Select action
        if "bind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.bind_category_project(project_key, category_key)
        elif "unbind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.unbind_category_project(project_key, category_key)

        return self.show_categories(req, context_key, context_data)

    def show_categories(self, req, context_key, context_data):
        """ Shows those categories that belongs into project and context
            that is given.
        """
        context_key = safe_int(context_key)

        if (context_key in context_data['combined_context_ids']
            or context_key in context_data['autocomplete_context_ids']):
            raise TracError('Cannot show combined categories')
        context_id = context_key
        # categories for main context are stored to index -1
        if context_key == context_data['main_context_id']:
            context_id = -1

        cats_per_context, project_cats_per_context = self._get_categories_data(context_data)
        data = {
            'context': context_data['context_by_id'][context_key],
            'categories': cats_per_context[context_id],
            'project_categories': project_cats_per_context[context_id],
            'env': req.base_path
        }

        return 'categories.html', data, None

    def add_and_bind_category(self, req, context_data):
        """
        Called by render_admin_panel
        """
        req.perm.require('TRAC_ADMIN')
        if req.method != 'POST':
            raise TracError('POST request required')
        category_name = req.args.get('category_name', '').strip()

        cat = self.categ.get_category_by_name(category_name)
        if not cat:
            context_id = safe_int(req.args.get('context_id', '').strip())

            if not context_id or context_id not in context_data['context_by_id']:
                add_warning(req, _("Invalid context"))
                return

            if not category_name:
                add_warning(req, _("Invalid category"))
                return

            context = context_data['context_by_id'][context_id]
            if context.edit_type != context.EDIT_TYPE_ADD:
                add_warning(req, _("Adding category into context %(context_name) is not allowed",
                    context_name=context.name))
                return

            try:
                self.categ.add_category(category_name, category_name, context_id, None)
                cat = self.categ.get_category_by_name(category_name)
            except Exception as e:
                add_warning(req, _('Category %(what)s cannot be added.',
                    what = category_name) + _(str(e)))
                return

        project_key = context_data['project_key']
        if cat and project_key and self.categ.bind_category_project(project_key, cat.category_id):
            add_notice(req, _('Category %(what)s has been added.',
                               what = category_name))
        else:
            add_warning(req, _('Category %(what)s cannot be added.',
                               what = category_name))

    def _get_context_data(self, context_id = None):
        """

        Returns dict with keys and values (type means here context.admin_type)::
            combined_context_ids: ids of contexts of type combined but not autocomplete
            not_separate_context_ids: set of ids of contexts of type combined, main, or autocomplete
            autocomplete_context_ids: ids of contexts of type autocomplete
            main_context_id: id of the context with type main, defaults to -1
            context_by_id: context_id => context mapping
            all_contexts: all contexts
            involved_context_ids: ids of contexts for which the categories are needed to be fetched
            context_id: safe context id of the request

        :returns: dict with data
        """
        main_context_id = -1

        combined_context_ids = []
        autocomplete_context_ids = []
        not_separate_context_ids = set([])
        context_by_id = {}
        all_contexts = self.categ.get_contexts()
        context_id = safe_int(context_id)
        project_key = Project.get(self.env).id

        for context in all_contexts:
            context_by_id[context.context_id] = context
            if context.admin_type == context.ADMIN_TYPE_MAIN:
                if main_context_id != -1:
                    self.log.warning("Invalid category data: Duplicate main contexts")
                    context.admin_type = context.ADMIN_TYPE_COMBINED
                else:
                    main_context_id = context.context_id
                    not_separate_context_ids.add(context.context_id)
            if context.admin_type == context.ADMIN_TYPE_COMBINED:
                combined_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                autocomplete_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
        if main_context_id == -1:
            self.log.warning('Invalid category data: Main context not set!')

        involved_context_ids = []
        if context_id:
            if context_id not in context_by_id:
                raise TracError('Invalid context id provided %s' %context_id.__class__)
            if context_id == main_context_id:
                involved_context_ids = [main_context_id]+combined_context_ids+autocomplete_context_ids
            else:
                involved_context_ids = [context_id]
        else:
            involved_context_ids = [context.context_id for context in all_contexts]
        data = {'project_key': project_key,
           'autocomplete_context_ids': autocomplete_context_ids,
           'combined_context_ids': combined_context_ids,
           'not_separate_context_ids': not_separate_context_ids,
           'main_context_id': main_context_id,
           'context_by_id': context_by_id,
           'all_contexts': all_contexts,
           'involved_context_ids': involved_context_ids,
           'context_id': context_id}
        return data

    def _get_categories_data(self, context_data):
        """
        Returns 'categories_per_context' and 'project_categories_per_context',
        both being dictionaries with context_id keys and values, which are
        dictionaries with category_id keys and category values.
        """
        context_by_id = context_data['context_by_id']
        main_context_id = context_data['main_context_id']
        project_key = context_data['project_key']
        categories_per_context = {-1: {}}
        project_categories_per_context = {-1: {}}
        for i_context_id in context_data['involved_context_ids']:
            context = context_by_id[i_context_id]
            if context.admin_type in (context.ADMIN_TYPE_MAIN, context.ADMIN_TYPE_COMBINED):
                categories_per_context[-1].update(
                    self.categ.get_all_categories_in_context(i_context_id))
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id)
                    )
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                # categories_per_context[i_context_id] -- don't show anywhere!
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id))
            else:
                categories_per_context[i_context_id] = \
                    self.categ.get_all_categories_in_context(i_context_id)
                project_categories_per_context[i_context_id] = \
                    self._get_categories_by_project(project_key, i_context_id)
        categories_per_context[main_context_id] = categories_per_context[-1]
        project_categories_per_context[main_context_id] = project_categories_per_context[-1]

        return categories_per_context, project_categories_per_context

    def _get_categories_by_project(self, project_key, context_id):
        category_dict = {}
        for category in self.categ.get_categories_by_project(project_key, context_id):
            category_dict[category.category_id] = category
        return category_dict

    def _get_context_name(self, context_key, all_contexts = None):
        if not all_contexts:
            all_contexts = self.categ.get_contexts()

        for c in all_contexts:
            if c.context_id == int(context_key):
                return c.name
        return ""
Esempio n. 8
0
class CategoryEditorAdminPanel(Component):
    """ Trac component for editing category structure
    """
    implements(IAdminPanelProvider)

    def get_admin_panels(self, req):
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', _('General'), 'categoryeditor', _('Edit categories'))

    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require("TRAC_ADMIN")
        self.categorystore = CQDECategoryStore()

        data = {}
        if req.method == 'POST':
            if req.args.get('add'):
                self.add_category(req)
            if req.args.get('remove'):
                self.remove_category(req)
            if req.args.get('edit'):
                self.edit_category(req)
            if req.args.get('move'):
                self.move_category_to_new_context(req)
            if req.args.get('merge'):
                self.merge_to_category(req)
            if req.args.get('reposition'):
                self.move_category_to_new_parent(req)

        contexts = self.categorystore.get_contexts()
        context_by_id = {}
        categories_per_context = {}
        for context in contexts:
            context_by_id[context.context_id] = context
            categories_per_context[context.context_id] = \
                self.categorystore.get_all_categories_in_context(context.context_id)
        is_rdf_description = lambda x: x.startswith('http://')

        data['context_by_id'] = context_by_id
        data['categories_per_context'] = categories_per_context
        data['envurl'] = req.base_path
        data['invalid_categories'] = self.categorystore.get_categories_with_invalid_context()
        data['is_rdf_description'] = is_rdf_description
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        return 'admin_categoryeditor.html', data

    def add_category(self, req):
        req.perm.require('TRAC_ADMIN')
        context = req.args.get('context', '').strip()
        category = req.args.get('category', '').strip()
        parent = req.args.get('parent', '').strip()

        if not context or not category:
            return
        if parent == "NONE" or parent == '':
            parent = None

        try:
            self.categorystore.add_category(category, category, context, parent)
            add_notice(req, _('Category %(what)s has been added.',
                              what=category))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be added. ',
                               what=category) + _(str(e)))

    def remove_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category = self._translate_req_to_category(req, 'removed');

        if not category:
            return

        try:
            self.categorystore.remove_category(category.category_id)
            add_notice(req, _('Category has been removed.'))
        except Exception as e:
            add_warning(req, _('Category was not removed. ') + _(str(e)))

    def edit_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category_id = req.args.get('edited_category_id', '')
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(category_id)
        if not category_id or not category:
            add_warning(req, _('Invalid category id provided.'))
            return
        category_name = req.args.get('edited_category_name', '')
        category_description = req.args.get('edited_category_description', '')
        if not category_name or not category_description:
            add_warning(req, _('Category name and description cannot be empty. "%s" "%s"'%(category_name, category_description)))
            return

        if category.name == category_name and category.description == category_description:
            add_warning(req, _('Category name and description are already as requested.'))
            return

        try:
            self.categorystore.edit_category(category.category_id, category_name, category_description)
            add_notice(req, _('Category has been edited.'))
        except Exception as e:
            add_warning(req, _('Category was not edited. ') + _(str(e)))


    def move_category_to_new_context(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'moved_category_id' or 'moved_category_name'
        category = self._translate_req_to_category(req, 'moved')

        if not new_context or not category:
            return

        try:
            self.categorystore.move_category_to_root_of_context(category.category_id, new_context)
            add_notice(req, _('Category %(what)s has been moved.',
                              what=category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be moved. ',
                               what=category.name) + _(str(e)))

    def move_category_to_new_parent(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'repositioned_category_id' or 'repositioned_category_name'
        category = self._translate_req_to_category(req, 'repositioned')
        root_category =  req.args.get('root_category')
        parent_category_id = None
        parent_category = None
        if not root_category:
            # possible params involved in req: 'new_parent_category_id' or 'new_parent_category_name'
            parent_category = self._translate_req_to_category(req, 'new_parent')
            if parent_category:
                parent_category_id = parent_category.category_id

        if not category or (not root_category and not parent_category):
            add_warning(req, _('Invalid parameters, nothing done. '))
            return

        if root_category:
            try:
                self.categorystore.move_category_to_root_of_context(category.category_id,
                    category.context)
                add_notice(req, _('Category %(what)s has been moved to be root category.',
                    what=category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to be root category. ',
                    what=category.name) + _(str(e)))
        else:
            try:
                self.categorystore.move_category_to_new_parent(category.category_id,
                    parent_category_id)
                add_notice(req, _('Category %(what)s has been moved to parent %(parent)s.',
                    what=category.name, parent=parent_category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to parent %(parent)s. ',
                    what=category.name, parent=parent_category.name) + _(str(e)))

    def merge_to_category(self, req):
        req.perm.require('TRAC_ADMIN')

        # possible params involved in req: target_category_name, target_category_id
        target_category = self._translate_req_to_category(req, 'target')
        # possible params involved in req: merged_category_id, merged_category_name
        merged_category = self._translate_req_to_category(req, 'merged')
        if not target_category or not merged_category:
            add_warning(req, _('Invalid merge category parameters: No corresponding category found.'))
            return

        try:
            self.categorystore.merge_category_to_category(merged_category.category_id,
                target_category.category_id)
            add_notice(req, _('Category %(merged)s has been merged to %(target)s.',
                merged = merged_category.name, target = target_category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s was not merged. ',
                what=merged_category.name) + _(str(e)))

    def _translate_req_to_category(self, req, prefix):
        """
        :returns: :py:class:`multiproject.core.categories.Category` or None
        """
        category_id = req.args.get(prefix + '_category_id', '').strip()
        category_name = req.args.get(prefix + '_category_name','').strip()
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(int(category_id))
        else:
            self.log.debug("searching %s with: %s " % (prefix, category_name))
            category = self.categorystore.get_category_by_name(category_name)
        return category
Esempio n. 9
0
class CategoryEditorAdminPanel(Component):
    """ Trac component for editing category structure
    """
    implements(IAdminPanelProvider)

    def get_admin_panels(self, req):
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', _('General'), 'categoryeditor', _('Edit categories'))

    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require("TRAC_ADMIN")
        self.categorystore = CQDECategoryStore()

        data = {}
        if req.method == 'POST':
            if req.args.get('add'):
                self.add_category(req)
            if req.args.get('remove'):
                self.remove_category(req)
            if req.args.get('edit'):
                self.edit_category(req)
            if req.args.get('move'):
                self.move_category_to_new_context(req)
            if req.args.get('merge'):
                self.merge_to_category(req)
            if req.args.get('reposition'):
                self.move_category_to_new_parent(req)

        contexts = self.categorystore.get_contexts()
        context_by_id = {}
        categories_per_context = {}
        for context in contexts:
            context_by_id[context.context_id] = context
            categories_per_context[context.context_id] = \
                self.categorystore.get_all_categories_in_context(context.context_id)
        is_rdf_description = lambda x: x.startswith('http://')

        data['context_by_id'] = context_by_id
        data['categories_per_context'] = categories_per_context
        data['envurl'] = req.base_path
        data['invalid_categories'] = self.categorystore.get_categories_with_invalid_context()
        data['is_rdf_description'] = is_rdf_description
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        return 'admin_categoryeditor.html', data

    def add_category(self, req):
        req.perm.require('TRAC_ADMIN')
        context = req.args.get('context', '').strip()
        category = req.args.get('category', '').strip()
        parent = req.args.get('parent', '').strip()
        license_url = req.args.get('license_url', '').strip()

        if not context or not category:
            return
        if license_url:
            category = category + "#" + license_url

        
        if parent == "NONE" or parent == '':
            parent = None

        try:
            self.categorystore.add_category(category, category, context, parent)
            add_notice(req, _('Category %(what)s has been added.',
                              what=category))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be added. ',
                               what=category) + _(str(e)))

    def remove_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category = self._translate_req_to_category(req, 'removed');

        if not category:
            return

        try:
            self.categorystore.remove_category(category.category_id)
            add_notice(req, _('Category has been removed.'))
        except Exception as e:
            add_warning(req, _('Category was not removed. ') + _(str(e)))

    def edit_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category_id = req.args.get('edited_category_id', '')
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(category_id)
        if not category_id or not category:
            add_warning(req, _('Invalid category id provided.'))
            return
        category_name = req.args.get('edited_category_name', '')
        category_description = req.args.get('edited_category_description', '')
        if len(category_description.split("#")) == 2:
            category_name = category_name + "#" + category_description.split("#")[1]
        if not category_name or not category_description:
            add_warning(req, _('Category name and description cannot be empty. "%s" "%s"'%(category_name, category_description)))
            return

        if category.name == category_name and category.description == category_description:
            add_warning(req, _('Category name and description are already as requested.'))
            return

        try:
            self.categorystore.edit_category(category.category_id, category_name, category_description)
            add_notice(req, _('Category has been edited.'))
        except Exception as e:
            add_warning(req, _('Category was not edited. ') + _(str(e)))


    def move_category_to_new_context(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'moved_category_id' or 'moved_category_name'
        category = self._translate_req_to_category(req, 'moved')

        if not new_context or not category:
            return

        try:
            self.categorystore.move_category_to_root_of_context(category.category_id, new_context)
            add_notice(req, _('Category %(what)s has been moved.',
                              what=category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be moved. ',
                               what=category.name) + _(str(e)))

    def move_category_to_new_parent(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'repositioned_category_id' or 'repositioned_category_name'
        category = self._translate_req_to_category(req, 'repositioned')
        root_category =  req.args.get('root_category')
        parent_category_id = None
        parent_category = None
        if not root_category:
            # possible params involved in req: 'new_parent_category_id' or 'new_parent_category_name'
            parent_category = self._translate_req_to_category(req, 'new_parent')
            if parent_category:
                parent_category_id = parent_category.category_id

        if not category or (not root_category and not parent_category):
            add_warning(req, _('Invalid parameters, nothing done. '))
            return

        if root_category:
            try:
                self.categorystore.move_category_to_root_of_context(category.category_id,
                    category.context)
                add_notice(req, _('Category %(what)s has been moved to be root category.',
                    what=category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to be root category. ',
                    what=category.name) + _(str(e)))
        else:
            try:
                self.categorystore.move_category_to_new_parent(category.category_id,
                    parent_category_id)
                add_notice(req, _('Category %(what)s has been moved to parent %(parent)s.',
                    what=category.name, parent=parent_category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to parent %(parent)s. ',
                    what=category.name, parent=parent_category.name) + _(str(e)))

    def merge_to_category(self, req):
        req.perm.require('TRAC_ADMIN')

        # possible params involved in req: target_category_name, target_category_id
        target_category = self._translate_req_to_category(req, 'target')
        # possible params involved in req: merged_category_id, merged_category_name
        merged_category = self._translate_req_to_category(req, 'merged')
        if not target_category or not merged_category:
            add_warning(req, _('Invalid merge category parameters: No corresponding category found.'))
            return

        try:
            self.categorystore.merge_category_to_category(merged_category.category_id,
                target_category.category_id)
            add_notice(req, _('Category %(merged)s has been merged to %(target)s.',
                merged = merged_category.name, target = target_category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s was not merged. ',
                what=merged_category.name) + _(str(e)))

    def _translate_req_to_category(self, req, prefix):
        """
        :returns: :py:class:`multiproject.core.categories.Category` or None
        """
        category_id = req.args.get(prefix + '_category_id', '').strip()
        category_name = req.args.get(prefix + '_category_name','').strip()
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(int(category_id))
        else:
            self.log.debug("searching %s with: %s " % (prefix, category_name))
            category = self.categorystore.get_category_by_name(category_name)
        return category
Esempio n. 10
0
    def _get_project_categories(self, project):
        """
        Create list of categories. License, Language and Development status are shown separately
        and are therefore taken out from the category listing.
        """
        # Get api
        cs = CQDECategoryStore()

        # Get categories and specialcase contexts
        categories = cs.get_all_project_categories(project.id)
        contexts = cs.get_contexts()

        combined_categories = []
        separated_categories_per_context = {}

        categories_by_any_context_id = {}
        context_by_id = {}

        # Setup the contexts dicts
        for context in contexts:
            context_by_id[context.context_id] = context
            if context.summary_name:
                # If context has summary name, it is shown as separated in the summary page
                new_list = []
                categories_by_any_context_id[context.context_id] = new_list
                separated_categories_per_context[context.context_id] = new_list
            else:
                categories_by_any_context_id[
                    context.context_id] = combined_categories

        sort_opts = {
            'key': lambda c: c.name,
            'cmp': lambda x, y: cmp(x.lower(), y.lower())
        }

        # Now, categories_by_any_context_id has key for each context id
        for category in categories:
            categories_by_any_context_id[category.context].append(category)
        for context_id in separated_categories_per_context:
            separated_categories_per_context[context_id].sort(**sort_opts)

        combined_categories.sort(**sort_opts)

        # Sort alphabetically, case-insensitively
        context_order = [
            c.context_id
            for c in sorted(map(lambda id: context_by_id[id],
                                separated_categories_per_context.keys()),
                            key=lambda c: c.summary_name)
        ]

        languages = []
        for context_id in separated_categories_per_context:
            context = context_by_id[context_id]
            if (context.summary_name and context.summary_name.lower()
                    in ('language', 'natural language')):
                # Here, we expect that the description contains the language tag.
                # http://www.w3schools.com/tags/att_meta_http_equiv.asp
                # http://www.w3.org/TR/2011/WD-html-markup-20110113/meta.http-equiv.content-language.html
                languages = [
                    c.description
                    for c in separated_categories_per_context[context_id]
                ]

        return (combined_categories, separated_categories_per_context,
                context_by_id, context_order, languages)