Commits

joberschweiber committed 67de72f

Add a new admin tab to each repository

Comments (0)

Files changed (4)

rhodecode/config/routing.py

                  controller='forks', action='forks',
                  conditions=dict(function=check_repo))
 
+    rmap.connect('repo_admin', '/{repo_name:.*}/admin',
+                 controller='settings', action='repo_admin',
+                 conditions=dict(function=check_repo))
+
+    rmap.connect('repo_admin_update', '/{repo_name:.*}/admin/update',
+                 controller='settings', action='repo_admin_update',
+                 conditions=dict(function=check_repo, method=['PUT']))
+
     return rmap

rhodecode/controllers/settings.py

 from rhodecode.lib.base import BaseRepoController, render
 from rhodecode.lib.utils import invalidate_cache, action_logger
 
-from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
+from rhodecode.model.forms import RepoSettingsForm, RepoForkForm, RepoForm
 from rhodecode.model.repo import RepoModel
-from rhodecode.model.db import Group
+from rhodecode.model.db import Group, IssueLabel, Issue, IssueComment, IssueLabelPreset
+
+from sqlalchemy.sql.expression import and_, or_
 
 log = logging.getLogger(__name__)
 
                     repo_name, category='error')
 
         return redirect(url('home'))
+
+
+    def __build_label_text(self, presets, include_key=False):
+        parts = []
+        for p in presets:
+            s = ''
+            if include_key:
+                s += p.key + '-'
+            s += p.value
+            if p.description != None:
+                s += (' ' * (29 - len(s))) + ' = ' + p.description
+            parts.append(s)
+        return "\n".join(parts)
+
+    def __load_defaults(self):
+        c.repo_groups = Group.groups_choices()
+        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
+
+    def __load_repo_admin_data(self, repo_name):
+        self.__load_defaults()
+
+        open_stati = self.sa.query(IssueLabelPreset).filter(and_(IssueLabelPreset.repo_id == c.rhodecode_db_repo.repo_id,
+            IssueLabelPreset.kind == IssueLabelPreset.PRESET_KIND_STATUS_OPEN)).all()
+        c.open_stati_text = self.__build_label_text(open_stati)
+
+        closed_stati = self.sa.query(IssueLabelPreset).filter(and_(IssueLabelPreset.repo_id == c.rhodecode_db_repo.repo_id,
+            IssueLabelPreset.kind == IssueLabelPreset.PRESET_KIND_STATUS_CLOSED)).all()
+        c.closed_stati_text = self.__build_label_text(closed_stati)
+
+        common_labels = self.sa.query(IssueLabelPreset).filter(and_(IssueLabelPreset.repo_id == c.rhodecode_db_repo.repo_id,
+            IssueLabelPreset.kind == IssueLabelPreset.PRESET_KIND_LABEL)).all()
+        c.common_labels_text = self.__build_label_text(common_labels, True)
+
+        return RepoModel()._get_defaults(repo_name)
+
+    def __store_preset(self, kind, key, value, desc, repo_id):
+        preset = IssueLabelPreset()
+        preset.repo_id = repo_id
+        preset.kind = kind
+        preset.key = key
+        preset.value = value
+        preset.description = desc
+        self.sa.add(preset)
+
+    def __extract_labels_descriptions(self, var):
+        lines = request.POST[var].split("\n")
+        labels_descs = {}
+        for l in lines:
+            if '=' in l:
+                parts = l.split('=', 1)
+                labels_descs[parts[0].strip()] = parts[1].strip()
+            else:
+                labels_descs[l.strip()] = None
+        return labels_descs
+
+    def __synchronize_stati(self, open_stati, labels_descs, repo_id, kind):
+        for o in open_stati:
+            if not o.value in labels_descs.keys():
+                self.sa.delete(o)
+            else:
+                if labels_descs[o.value] != o.description:
+                    o.description = labels_descs[o.value]
+                    self.sa.add(o)
+                labels_descs.pop(o.value)
+        for k,v in labels_descs.items():
+            self.__store_preset(kind, 'Status', k, v, repo_id)
+
+    def __update_issue_presets(self, repo_name):
+        dbrepo = c.rhodecode_db_repo
+
+        open_stati = self.sa.query(IssueLabelPreset).filter(and_(IssueLabelPreset.repo_id == dbrepo.repo_id,
+            IssueLabelPreset.kind == IssueLabelPreset.PRESET_KIND_STATUS_OPEN)).all()
+        labels_descs = self.__extract_labels_descriptions('open_stati')
+        self.__synchronize_stati(open_stati, labels_descs, dbrepo.repo_id, IssueLabelPreset.PRESET_KIND_STATUS_OPEN)
+
+        closed_stati = self.sa.query(IssueLabelPreset).filter(and_(IssueLabelPreset.repo_id == dbrepo.repo_id,
+            IssueLabelPreset.kind == IssueLabelPreset.PRESET_KIND_STATUS_CLOSED)).all()
+        labels_descs = self.__extract_labels_descriptions('closed_stati')
+        self.__synchronize_stati(closed_stati, labels_descs, dbrepo.repo_id, IssueLabelPreset.PRESET_KIND_STATUS_CLOSED)
+
+        common_labels = self.sa.query(IssueLabelPreset).filter(and_(IssueLabelPreset.repo_id == dbrepo.repo_id,
+            IssueLabelPreset.kind == IssueLabelPreset.PRESET_KIND_LABEL)).all()
+        labels_descs = self.__extract_labels_descriptions('common_labels')
+
+        for l in common_labels:
+            rendered = l.rendered
+            if rendered in labels_descs.keys():
+                if labels_descs[rendered] != l.description:
+                    l.description = labels_descs[rendered]
+                    self.sa.add(l)
+                labels_descs.pop(rendered)
+            else:
+                self.sa.delete(l)
+        for r,d in labels_descs.items():
+            k, v = '', None
+            if '-' in r:
+                k, v = r.split('-', 1)
+            else:
+                k = r
+            self.__store_preset(IssueLabelPreset.PRESET_KIND_LABEL, k, v, d, dbrepo.repo_id)
+        self.sa.commit()
+
+    @HasRepoPermissionAnyDecorator('hg.admin', 'repository.admin')
+    def repo_admin(self, repo_name):
+        defaults = self.__load_repo_admin_data(repo_name)
+
+        return htmlfill.render(
+                render('settings/repo_admin.html'),
+                defaults=defaults,
+                encoding='UTF-8',
+                force_defaults=False)
+
+    @HasRepoPermissionAnyDecorator('hg.admin', 'repository.admin')
+    def repo_admin_update(self, repo_name):
+        self.__load_defaults()
+        repo_model = RepoModel()
+        changed_name = repo_name
+        _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
+                repo_groups=c.repo_groups_choices)()
+        try:
+            request.POST['user'] = c.rhodecode_db_repo.user.username
+            form_result = _form.to_python(dict(request.POST))
+            repo = repo_model.update(repo_name, form_result)
+            invalidate_cache('get_repo_cached_%s' % repo_name)
+
+            changed_name = repo.repo_name
+
+            self.__update_issue_presets(repo_name)
+
+            h.flash(_('Repository %s updated successfully' % repo_name), category='success')
+
+            action_logger(self.rhodecode_user, 'admin_updated_repo', changed_name, '', self.sa)
+        except formencode.Invalid, errors:
+            defaults = self.__load_repo_admin_data(repo_name)
+            defaults.update(errors.value)
+            print errors.error_dict
+            return htmlfill.render(
+                    render('settings/repo_admin.html'),
+                    defaults=defaults,
+                    errors=errors.error_dict or {},
+                    prefix_error=False,
+                    encoding='UTF-8')
+
+        except Exception:
+            log.error(traceback.format_exc())
+            h.flash(_('Error occured during update of repository %s' % repo_name), category='error')
+        
+        return redirect(url('repo_admin', repo_name=changed_name))

rhodecode/templates/base/base.html

     <li class="${current_class('code')}">${h.link_to(_('Code'), h.url('files_home', repo_name=c.repo_name))}</li>
     <li class="${current_class('history')}">${h.link_to(_('History'), h.url('changelog_home', repo_name=c.repo_name))}</li>
     <li class="${current_class('issues')}">${h.link_to(_('Issues'), h.url('issues_home', repo_name=c.repo_name))}</li>
+    %if h.HasRepoPermissionAny('hg.admin', 'repository.admin'):
+    <li class="${current_class('admin')}">${h.link_to(_('Admin'), h.url('repo_admin', repo_name=c.repo_name))}</li>
+    %endif
 
     <li class="right">${_('Clone URL:')} <span class="strong">${c.clone_repo_url}</span></li>
   </ul>

rhodecode/templates/settings/repo_admin.html

+## -*- coding: utf-8 -*-
+<%inherit file="/base/base.html" />
+
+<%def name="title()">
+    ${c.repo_name} ${_('Settings')}
+</%def>
+
+<%def name="page_nav()">
+    ${self.menu('admin')}
+</%def>
+
+<%def name="main()">
+
+${h.form(url('repo_admin_update', repo_name=c.repo_name), method='put', class_='form-horizontal')}
+<div class="tabbable tabs-left">
+    <ul class="nav nav-tabs">
+        <li class="active"><a href="#1" data-toggle="tab">Settings</a></li>
+        <li><a href="#issues" data-toggle="tab">Issues</a></li>
+        <li><a href="#advanced" data-toggle="tab">Advanced</a></li>
+    </ul>
+
+    <div class="tab-content">
+        <div class="tab-pane active" id="1">
+            <div class="control-group">
+                <label class="control-label" for="repo_name">${_('Name')}</label>
+                <div class="controls">
+                    ${h.text('repo_name', class_='input-xlarge')}
+                </div>
+            </div>
+
+            <div class="control-group">
+                <label class="control-label" for="repo_group">${_('Repository group')}</label>
+                <div class="controls">
+                    ${h.select('repo_group', '', c.repo_groups, class_='input-xlarge')}
+                    <span class="help-block">
+                        ${_('Changing the repository group also changes the clone URL.')}
+                    </span>
+                </div>
+            </div>
+
+            <div class="control-group">
+                <label class="control-label" for="description">${_('Description')}</label>
+                <div class="controls">
+                    ${h.text('description', class_='input-xlarge')}
+                    <span class="help-block">
+                        ${_('Keep it short and to the point. Use a README file for longer descriptions.')}
+                    </span>
+                </div>
+            </div>
+
+            <div class="control-group">
+                <div class="controls">
+                    <div class="checkbox">
+                        ${h.checkbox('private')}
+                        ${_('This is a private repository')}
+                    </div>
+                    <span class="help-block">
+                        ${_('Private repositories are only visible to people explicitly added as collaborators.')}
+                    </span>
+                </div>
+            </div>
+        </div>
+
+        <div class="tab-pane" id="issues">
+            <div class="control-group comment-form">
+                <label class="control-label" for="open_stati">Open status values</label>
+                <div class="controls">
+                    ${h.textarea('open_stati', cols=75, rows=10, content=c.open_stati_text, _class='value-box')}
+                    <span class="help-block">
+                        ${_('List one element per line')}
+                    </span>
+                </div>
+            </div>
+
+            <div class="control-group comment-form">
+                <label class="control-label" for="closed_stati">Closed status values</label>
+                <div class="controls">
+                    ${h.textarea('closed_stati', cols=75, rows=10, content=c.closed_stati_text, _class='value-box')}
+                    <span class="help-block">
+                        ${_('List one element per line')}
+                    </span>
+                </div>
+            </div>
+
+            <div class="control-group comment-form">
+                <label class="control-label" for="common_labels">Common labels</label>
+                <div class="controls">
+                    ${h.textarea('common_labels', cols=75, rows=10, content=c.common_labels_text, _class='value-box')}
+                    <span class="help-block">
+                        ${_('List one element per line.')}
+                    </span>
+                </div>
+            </div>
+        </div>
+
+        <div class="tab-pane" id="advanced">
+            <div class="control-group">
+                <label class="control-label" for="clone_uri">${_('Clone URI')}</label>
+                <div class="controls">
+                    ${h.text('clone_uri', class_='input-xlarge')}
+                </div>
+            </div>
+
+            <div class="control-group">
+                <label class="control-label" for="repo_type">${_('Type')}</label>
+                <div class="controls">
+                    ${h.select('repo_type', 'hg', c.backends, class_='input-xlarge')}
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<div class="form-actions">
+    ${h.submit('save', _('Save'), class_='btn btn-primary')}
+</div>
+${h.end_form()}
+
+</%def>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.