Exemplo n.º 1
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 ""
Exemplo n.º 2
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
Exemplo n.º 3
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 ""
Exemplo n.º 4
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