Example #1
0
        def _c():
            log.debug('generating switcher repo/groups list')
            all_repos = Repository.query(sorted=True).all()
            repo_iter = self.scm_model.get_repos(all_repos)
            all_groups = RepoGroup.query(sorted=True).all()
            repo_groups_iter = self.scm_model.get_repo_groups(all_groups)

            res = [{
                    'text': _('Groups'),
                    'children': [
                       {'id': obj.group_name,
                        'text': obj.group_name,
                        'type': 'group',
                        'obj': {}}
                       for obj in repo_groups_iter
                    ],
                   },
                   {
                    'text': _('Repositories'),
                    'children': [
                       {'id': obj.repo_name,
                        'text': obj.repo_name,
                        'type': 'repo',
                        'obj': obj.get_dict()}
                       for obj in repo_iter
                    ],
                   }]

            data = {
                'more': False,
                'results': res,
            }
            return data
Example #2
0
    def my_account_password(self):
        c.active = 'password'
        self.__load_data()

        managed_fields = auth_modules.get_managed_fields(c.user)
        c.can_change_password = '******' not in managed_fields

        if request.POST and c.can_change_password:
            _form = PasswordChangeForm(request.authuser.username)()
            try:
                form_result = _form.to_python(request.POST)
                UserModel().update(request.authuser.user_id, form_result)
                Session().commit()
                h.flash(_("Successfully updated password"), category='success')
            except formencode.Invalid as errors:
                return htmlfill.render(
                    render('admin/my_account/my_account.html'),
                    defaults=errors.value,
                    errors=errors.error_dict or {},
                    prefix_error=False,
                    encoding="UTF-8",
                    force_defaults=False)
            except Exception:
                log.error(traceback.format_exc())
                h.flash(_('Error occurred during update of user password'),
                        category='error')
        return render('admin/my_account/my_account.html')
Example #3
0
 def lnk(rev, repo_name):
     lazy_cs = False
     title_ = None
     url_ = '#'
     if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict):
         if rev.op and rev.ref_name:
             if rev.op == 'delete_branch':
                 lbl = _('Deleted branch: %s') % rev.ref_name
             elif rev.op == 'tag':
                 lbl = _('Created tag: %s') % rev.ref_name
             else:
                 lbl = 'Unknown operation %s' % rev.op
         else:
             lazy_cs = True
             lbl = rev.short_id[:8]
             url_ = url('changeset_home', repo_name=repo_name,
                        revision=rev.raw_id)
     else:
         # changeset cannot be found - it might have been stripped or removed
         lbl = rev[:12]
         title_ = _('Changeset %s not found') % lbl
     if parse_cs:
         return link_to(lbl, url_, title=title_, **{'data-toggle': 'tooltip'})
     return link_to(lbl, url_, class_='lazy-cs' if lazy_cs else '',
                    **{'data-raw_id':rev.raw_id, 'data-repo_name':repo_name})
Example #4
0
 def repo_refs_data(self, repo_name):
     repo = Repository.get_by_repo_name(repo_name).scm_instance
     res = []
     _branches = repo.branches.items()
     if _branches:
         res.append({
             'text': _('Branch'),
             'children': [{'id': rev, 'text': name, 'type': 'branch'} for name, rev in _branches]
         })
     _closed_branches = repo.closed_branches.items()
     if _closed_branches:
         res.append({
             'text': _('Closed Branches'),
             'children': [{'id': rev, 'text': name, 'type': 'closed-branch'} for name, rev in _closed_branches]
         })
     _tags = repo.tags.items()
     if _tags:
         res.append({
             'text': _('Tag'),
             'children': [{'id': rev, 'text': name, 'type': 'tag'} for name, rev in _tags]
         })
     _bookmarks = repo.bookmarks.items()
     if _bookmarks:
         res.append({
             'text': _('Bookmark'),
             'children': [{'id': rev, 'text': name, 'type': 'book'} for name, rev in _bookmarks]
         })
     data = {
         'more': False,
         'results': res
     }
     return data
Example #5
0
 def create(self):
     c.default_extern_type = User.DEFAULT_AUTH_TYPE
     c.default_extern_name = ''
     user_model = UserModel()
     user_form = UserForm()()
     try:
         form_result = user_form.to_python(dict(request.POST))
         user = user_model.create(form_result)
         action_logger(request.authuser, 'admin_created_user:%s' % user.username,
                       None, request.ip_addr)
         h.flash(_('Created user %s') % user.username,
                 category='success')
         Session().commit()
     except formencode.Invalid as errors:
         return htmlfill.render(
             render('admin/users/user_add.html'),
             defaults=errors.value,
             errors=errors.error_dict or {},
             prefix_error=False,
             encoding="UTF-8",
             force_defaults=False)
     except UserCreationError as e:
         h.flash(e, 'error')
     except Exception:
         log.error(traceback.format_exc())
         h.flash(_('Error occurred during creation of user %s') \
                 % request.POST.get('username'), category='error')
     raise HTTPFound(location=url('edit_user', id=user.user_id))
Example #6
0
 def delete(self, category_id, **kw):
     try:
         app_globals.shop.category.delete(category_id)
         flash(_('Category deleted'))
     except CategoryAssignedToProductException:
         flash(_('Is impossible to delete a category assigned to product'), 'error')
     return redirect(plug_url('stroller2', '/manage/category/index'))
Example #7
0
    def delete(self, user, cur_user=None):
        if cur_user is None:
            cur_user = getattr(get_current_authuser(), 'username', None)
        user = User.guess_instance(user)

        if user.is_default_user:
            raise DefaultUserException(
                _("You can't remove this user since it is"
                  " crucial for the entire application"))
        if user.repositories:
            repos = [x.repo_name for x in user.repositories]
            raise UserOwnsReposException(
                _('User "%s" still owns %s repositories and cannot be '
                  'removed. Switch owners or remove those repositories: %s')
                % (user.username, len(repos), ', '.join(repos)))
        if user.repo_groups:
            repogroups = [x.group_name for x in user.repo_groups]
            raise UserOwnsReposException(_(
                'User "%s" still owns %s repository groups and cannot be '
                'removed. Switch owners or remove those repository groups: %s')
                % (user.username, len(repogroups), ', '.join(repogroups)))
        if user.user_groups:
            usergroups = [x.users_group_name for x in user.user_groups]
            raise UserOwnsReposException(
                _('User "%s" still owns %s user groups and cannot be '
                  'removed. Switch owners or remove those user groups: %s')
                % (user.username, len(usergroups), ', '.join(usergroups)))
        Session().delete(user)

        from kallithea.lib.hooks import log_delete_user
        log_delete_user(user.get_dict(), cur_user)
Example #8
0
File: root.py Project: mbbui/Jminee
    def activate(self, email, code):
        reg = Registration.get_inactive(email, code)
        if not reg:
            flash(_('Registration not found or already activated'))
            return redirect(self.mount_point)

        u = app_model.User(user_name=reg.user_name,
                           display_name=reg.user_name,
                           email_address=reg.email_address,
                           password=reg.password)

        hooks = config['hooks'].get('registration.before_activation', [])
        for func in hooks:
            func(reg, u)

        DBSession.add(u)

        reg.user = u
        reg.password = '******'
        reg.activated = datetime.now()

        hooks = config['hooks'].get('registration.after_activation', [])
        for func in hooks:
            func(reg, u)

        flash(_('Account succesfully activated'))
        return redirect('/')
Example #9
0
    def update_perms(self, group_name):
        """
        Update permissions for given repository group

        :param group_name:
        """

        c.repo_group = RepoGroup.guess_instance(group_name)
        valid_recursive_choices = ['none', 'repos', 'groups', 'all']
        form_result = RepoGroupPermsForm(valid_recursive_choices)().to_python(request.POST)
        if not request.authuser.is_admin:
            if self._revoke_perms_on_yourself(form_result):
                msg = _('Cannot revoke permission for yourself as admin')
                h.flash(msg, category='warning')
                raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
        recursive = form_result['recursive']
        # iterate over all members(if in recursive mode) of this groups and
        # set the permissions !
        # this can be potentially heavy operation
        RepoGroupModel()._update_permissions(c.repo_group,
                                             form_result['perms_new'],
                                             form_result['perms_updates'],
                                             recursive)
        #TODO: implement this
        #action_logger(request.authuser, 'admin_changed_repo_permissions',
        #              repo_name, request.ip_addr)
        Session().commit()
        h.flash(_('Repository group permissions updated'), category='success')
        raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
Example #10
0
    def delete_perms(self, id):
        try:
            obj_type = request.POST.get('obj_type')
            obj_id = None
            if obj_type == 'user':
                obj_id = safe_int(request.POST.get('user_id'))
            elif obj_type == 'user_group':
                obj_id = safe_int(request.POST.get('user_group_id'))

            if not request.authuser.is_admin:
                if obj_type == 'user' and request.authuser.user_id == obj_id:
                    msg = _('Cannot revoke permission for yourself as admin')
                    h.flash(msg, category='warning')
                    raise Exception('revoke admin permission on self')
            if obj_type == 'user':
                UserGroupModel().revoke_user_permission(user_group=id,
                                                        user=obj_id)
            elif obj_type == 'user_group':
                UserGroupModel().revoke_user_group_permission(target_user_group=id,
                                                              user_group=obj_id)
            Session().commit()
        except Exception:
            log.error(traceback.format_exc())
            h.flash(_('An error occurred during revoking of permission'),
                    category='error')
            raise HTTPInternalServerError()
Example #11
0
    def put_delete_undo(self, item_id):
        require_current_user_is_owner(int(item_id))

        item_id = int(item_id)
        content_api = ContentApi(tmpl_context.current_user, True, True) # Here we do not filter deleted items
        item = content_api.get_one(item_id, self._item_type, tmpl_context.workspace)
        try:
            next_url = tg.url('/workspaces/{}/folders/{}/threads/{}').format(tmpl_context.workspace_id,
                                                                             tmpl_context.folder_id,
                                                                             tmpl_context.thread_id)
            msg = _('{} undeleted.').format(self._item_type_label)
            content_api.undelete(item)
            content_api.save(item, ActionDescription.UNDELETION)

            tg.flash(msg, CST.STATUS_OK)
            tg.redirect(next_url)

        except ValueError as e:
            logger.debug(self, 'Exception: {}'.format(e.__str__))
            back_url = tg.url('/workspaces/{}/folders/{}/threads/{}').format(tmpl_context.workspace_id,
                                                                             tmpl_context.folder_id,
                                                                             tmpl_context.thread_id)
            msg = _('{} not un-deleted: {}').format(self._item_type_label, str(e))
            tg.flash(msg, CST.STATUS_ERROR)
            tg.redirect(back_url)
Example #12
0
    def create(self):
        users_group_form = UserGroupForm()()
        try:
            form_result = users_group_form.to_python(dict(request.POST))
            ug = UserGroupModel().create(name=form_result['users_group_name'],
                                         description=form_result['user_group_description'],
                                         owner=request.authuser.user_id,
                                         active=form_result['users_group_active'])

            gr = form_result['users_group_name']
            action_logger(request.authuser,
                          'admin_created_users_group:%s' % gr,
                          None, request.ip_addr)
            h.flash(h.literal(_('Created user group %s') % h.link_to(h.escape(gr), url('edit_users_group', id=ug.users_group_id))),
                category='success')
            Session().commit()
        except formencode.Invalid as errors:
            return htmlfill.render(
                render('admin/user_groups/user_group_add.html'),
                defaults=errors.value,
                errors=errors.error_dict or {},
                prefix_error=False,
                encoding="UTF-8",
                force_defaults=False)
        except Exception:
            log.error(traceback.format_exc())
            h.flash(_('Error occurred during creation of user group %s') \
                    % request.POST.get('users_group_name'), category='error')

        raise HTTPFound(location=url('users_groups'))
Example #13
0
    def put(self, current_password, new_password1, new_password2):
        if not tg.config.get('auth_is_internal'):
            raise HTTPForbidden()

        # FIXME - Allow only self password or operation for managers
        current_user = tmpl_context.current_user

        redirect_url = tg.lurl('/home')

        if not current_password or not new_password1 or not new_password2:
            tg.flash(_('Empty password is not allowed.'))
            tg.redirect(redirect_url)

        if current_user.validate_password(current_password) is False:
            tg.flash(_('The current password you typed is wrong'))
            tg.redirect(redirect_url)

        if new_password1!=new_password2:
            tg.flash(_('New passwords do not match.'))
            tg.redirect(redirect_url)

        current_user.password = new_password1
        pm.DBSession.flush()

        tg.flash(_('Your password has been changed'))
        tg.redirect(redirect_url)
Example #14
0
    def put(self, user_id, name, email, timezone, next_url=None):
        user_id = tmpl_context.current_user.user_id
        current_user = tmpl_context.current_user
        user_api = UserApi(current_user)
        assert user_id==current_user.user_id
        if next_url:
            next = tg.url(next_url)
        else:
            next = self.url()

        try:
            email_user = user_api.get_one_by_email(email)
            if email_user != current_user:
                tg.flash(_('Email already in use'), CST.STATUS_ERROR)
                tg.redirect(next)
        except NoResultFound:
            pass

        # Only keep allowed field update
        updated_fields = self._clean_update_fields({
            'name': name,
            'email': email,
            'timezone': timezone,
        })

        api = UserApi(tmpl_context.current_user)
        api.update(current_user, do_save=True, **updated_fields)
        tg.flash(_('profile updated.'))
        tg.redirect(next)
Example #15
0
    def delete(self, repo_name):
        repo_model = RepoModel()
        repo = repo_model.get_by_repo_name(repo_name)
        if not repo:
            h.not_mapped_error(repo_name)
            raise HTTPFound(location=url('repos'))
        try:
            _forks = repo.forks.count()
            handle_forks = None
            if _forks and request.POST.get('forks'):
                do = request.POST['forks']
                if do == 'detach_forks':
                    handle_forks = 'detach'
                    h.flash(_('Detached %s forks') % _forks, category='success')
                elif do == 'delete_forks':
                    handle_forks = 'delete'
                    h.flash(_('Deleted %s forks') % _forks, category='success')
            repo_model.delete(repo, forks=handle_forks)
            action_logger(request.authuser, 'admin_deleted_repo',
                repo_name, request.ip_addr)
            ScmModel().mark_for_invalidation(repo_name)
            h.flash(_('Deleted repository %s') % repo_name, category='success')
            Session().commit()
        except AttachedForksError:
            h.flash(_('Cannot delete repository %s which still has forks')
                        % repo_name, category='warning')

        except Exception:
            log.error(traceback.format_exc())
            h.flash(_('An error occurred during deletion of %s') % repo_name,
                    category='error')

        if repo.group:
            raise HTTPFound(location=url('repos_group_home', group_name=repo.group.group_name))
        raise HTTPFound(location=url('repos'))
Example #16
0
    def repo_check(self, repo_name):
        c.repo = repo_name
        task_id = request.GET.get('task_id')

        if task_id and task_id not in ['None']:
            from kallithea import CELERY_ON
            from kallithea.lib import celerypylons
            if CELERY_ON:
                task = celerypylons.result.AsyncResult(task_id)
                if task.failed():
                    raise HTTPInternalServerError(task.traceback)

        repo = Repository.get_by_repo_name(repo_name)
        if repo and repo.repo_state == Repository.STATE_CREATED:
            if repo.clone_uri:
                h.flash(_('Created repository %s from %s')
                        % (repo.repo_name, repo.clone_uri_hidden), category='success')
            else:
                repo_url = h.link_to(repo.repo_name,
                                     h.url('summary_home',
                                           repo_name=repo.repo_name))
                fork = repo.fork
                if fork is not None:
                    fork_name = fork.repo_name
                    h.flash(h.literal(_('Forked repository %s as %s')
                            % (fork_name, repo_url)), category='success')
                else:
                    h.flash(h.literal(_('Created repository %s') % repo_url),
                            category='success')
            return {'result': True}
        return {'result': False}
Example #17
0
    def update(self, id):
        _form = DefaultsForm()()

        try:
            form_result = _form.to_python(dict(request.POST))
            for k, v in form_result.iteritems():
                setting = Setting.create_or_update(k, v)
            Session().commit()
            h.flash(_('Default settings updated successfully'),
                    category='success')

        except formencode.Invalid as errors:
            defaults = errors.value

            return htmlfill.render(
                render('admin/defaults/defaults.html'),
                defaults=defaults,
                errors=errors.error_dict or {},
                prefix_error=False,
                encoding="UTF-8",
                force_defaults=False)
        except Exception:
            log.error(traceback.format_exc())
            h.flash(_('Error occurred during update of defaults'),
                    category='error')

        raise HTTPFound(location=url('defaults'))
Example #18
0
    def create(self):
        self.__load_defaults()

        # permissions for can create group based on parent_id are checked
        # here in the Form
        repo_group_form = RepoGroupForm(repo_groups=c.repo_groups)
        try:
            form_result = repo_group_form.to_python(dict(request.POST))
            gr = RepoGroupModel().create(
                group_name=form_result['group_name'],
                group_description=form_result['group_description'],
                parent=form_result['parent_group_id'],
                owner=request.authuser.user_id, # TODO: make editable
                copy_permissions=form_result['group_copy_permissions']
            )
            Session().commit()
            #TODO: in future action_logger(, '', '', '')
        except formencode.Invalid as errors:
            return htmlfill.render(
                render('admin/repo_groups/repo_group_add.html'),
                defaults=errors.value,
                errors=errors.error_dict or {},
                prefix_error=False,
                encoding="UTF-8",
                force_defaults=False)
        except Exception:
            log.error(traceback.format_exc())
            h.flash(_('Error occurred during creation of repository group %s') \
                    % request.POST.get('group_name'), category='error')
            parent_group_id = form_result['parent_group_id']
            #TODO: maybe we should get back to the main view, not the admin one
            raise HTTPFound(location=url('repos_groups', parent_group=parent_group_id))
        h.flash(_('Created repository group %s') % gr.group_name,
                category='success')
        raise HTTPFound(location=url('repos_group_home', group_name=gr.group_name))
Example #19
0
    def register(self, **kw):
        """
        User register
        """
        log.debug("Recibiendo de registro %s", kw)
        if request.identity:
            redirect('/')

        """ Adding new user """
        user = model.User()
        user.email_address = kw.get('email', None)
        user.password = kw.get('password', None)
        user.user_name = kw.get('username', None)
        model.DBSession.add(user)

        # Persisting
        from sqlalchemy.exc import IntegrityError
        try:
            model.DBSession.flush()
            forceUser(user.user_name)
            flash(_(u'Bienvenido %s, su cuenta ha sido creada con éxito' % user.user_name), 'alert alert-success')
            redirect('/')
        except IntegrityError as e:
            log.debug('Error:', e)
            model.DBSession.rollback()
            if e.orig[0] == 1062: 
                # User register already exits
                flash(_('Parece que ya tienes cuenta'))
                redirect('/login')
            else:
                raise  
Example #20
0
    def put(self, item_id, label='',content=''):
        # TODO - SECURE THIS
        workspace = tmpl_context.workspace

        try:
            api = ContentApi(tmpl_context.current_user)
            item = api.get_one(int(item_id), self._item_type, workspace)
            with new_revision(item):
                api.update_content(item, label, content)

                if not self._path_validation.validate_new_content(item):
                    return render_invalid_integrity_chosen_path(
                        item.get_label(),
                    )

                api.save(item, ActionDescription.REVISION)

            msg = _('{} updated').format(self._item_type_label)
            tg.flash(msg, CST.STATUS_OK)
            tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item.content_id))

        except SameValueError as e:
            msg = _('{} not updated: the content did not change').format(self._item_type_label)
            tg.flash(msg, CST.STATUS_WARNING)
            tg.redirect(self._err_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item_id))

        except ValueError as e:
            msg = _('{} not updated - error: {}').format(self._item_type_label, str(e))
            tg.flash(msg, CST.STATUS_ERROR)
            tg.redirect(self._err_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item_id))
Example #21
0
    def reset_request(self, **kw):
        user = model.provider.query(app_model.User, filters=dict(email_address=kw['email_address']))[1][0]
        password_frag = user.password[0:4]
        serializer = URLSafeSerializer(tg.config['beaker.session.secret'])
        serialized_data = serializer.dumps(dict(request_date=datetime.utcnow().strftime('%m/%d/%Y %H:%M'),
                                                email_address=kw['email_address'], password_frag=password_frag))

        password_reset_link = tg.url(self.mount_point + "/change_password/", params=dict(data=serialized_data),
                                     qualified=True)
        reset_password_config = tg.config.get('_pluggable_resetpassword_config')
        mail_body = reset_password_config.get('mail_body', _('''
We've received a request to reset the password for this account. 
Please click this link to reset your password:

%(password_reset_link)s

If you no longer wish to make the above change, or if you did not initiate this request, please disregard and/or delete this e-mail.
'''))

        email_data = {'sender': tg.config['resetpassword.email_sender'],
                      'subject': reset_password_config.get('mail_subject', _('Password reset request')),
                      'body': mail_body,
                      'rich': reset_password_config.get('mail_rich', '')}

        tg.hooks.notify('resetpassword.on_request', args=(user, email_data, password_reset_link))

        email_data['body'] = email_data['body'] % dict(password_reset_link=password_reset_link)
        email_data['rich'] = email_data['rich'] % dict(password_reset_link=password_reset_link)
        send_email(user.email_address, **email_data)
        flash(_('Password reset request sent'))
        return plug_redirect('resetpassword', '/')
Example #22
0
    def put(self, folder_id, label, can_contain_folders=False, can_contain_threads=False, can_contain_files=False, can_contain_pages=False):
        # TODO - SECURE THIS
        workspace = tmpl_context.workspace

        api = ContentApi(tmpl_context.current_user)
        next_url = ''

        try:
            folder = api.get_one(int(folder_id), ContentType.Folder, workspace)
            subcontent = dict(
                folder = True if can_contain_folders=='on' else False,
                thread = True if can_contain_threads=='on' else False,
                file = True if can_contain_files=='on' else False,
                page = True if can_contain_pages=='on' else False
            )
            if label != folder.label:
                # TODO - D.A. - 2015-05-25 - Allow to set folder description
                api.update_content(folder, label, folder.description)
            api.set_allowed_content(folder, subcontent)
            api.save(folder)

            tg.flash(_('Folder updated'), CST.STATUS_OK)

            next_url = self.url(folder.content_id)

        except Exception as e:
            tg.flash(_('Folder not updated: {}').format(str(e)), CST.STATUS_ERROR)
            next_url = self.url(int(folder_id))

        tg.redirect(next_url)
Example #23
0
    def put_delete(self, item_id):
        require_current_user_is_owner(int(item_id))

        # TODO - CHECK RIGHTS
        item_id = int(item_id)
        content_api = ContentApi(tmpl_context.current_user)
        item = content_api.get_one(item_id, self._item_type, tmpl_context.workspace)

        try:

            next_url = tg.url('/workspaces/{}/folders/{}/threads/{}').format(tmpl_context.workspace_id,
                                                                             tmpl_context.folder_id,
                                                                             tmpl_context.thread_id)
            undo_url = tg.url('/workspaces/{}/folders/{}/threads/{}/comments/{}/put_delete_undo').format(tmpl_context.workspace_id,
                                                                                                         tmpl_context.folder_id,
                                                                                                         tmpl_context.thread_id,
                                                                                                         item_id)

            msg = _('{} deleted. <a class="alert-link" href="{}">Cancel action</a>').format(self._item_type_label, undo_url)
            content_api.delete(item)
            content_api.save(item, ActionDescription.DELETION)

            tg.flash(msg, CST.STATUS_OK, no_escape=True)
            tg.redirect(next_url)

        except ValueError as e:
            back_url = tg.url('/workspaces/{}/folders/{}/threads/{}').format(tmpl_context.workspace_id,
                                                                             tmpl_context.folder_id,
                                                                             tmpl_context.thread_id)
            msg = _('{} not deleted: {}').format(self._item_type_label, str(e))
            tg.flash(msg, CST.STATUS_ERROR)
            tg.redirect(back_url)
Example #24
0
    def __get_cs(self, rev, silent_empty=False):
        """
        Safe way to get changeset if error occur it redirects to tip with
        proper message

        :param rev: revision to fetch
        :silent_empty: return None if repository is empty
        """

        try:
            return c.db_repo_scm_instance.get_changeset(rev)
        except EmptyRepositoryError as e:
            if silent_empty:
                return None
            url_ = url('files_add_home',
                       repo_name=c.repo_name,
                       revision=0, f_path='', anchor='edit')
            add_new = h.link_to(_('Click here to add new file'), url_, class_="alert-link")
            h.flash(h.literal(_('There are no files yet. %s') % add_new),
                    category='warning')
            raise HTTPNotFound()
        except (ChangesetDoesNotExistError, LookupError):
            msg = _('Such revision does not exist for this repository')
            h.flash(msg, category='error')
            raise HTTPNotFound()
        except RepositoryError as e:
            h.flash(safe_str(e), category='error')
            raise HTTPNotFound()
Example #25
0
def _ignorews_url(GET, fileid=None):
    fileid = str(fileid) if fileid else None
    params = defaultdict(list)
    _update_with_GET(params, GET)
    lbl = _('Show whitespace')
    ig_ws = get_ignore_ws(fileid, GET)
    ln_ctx = get_line_ctx(fileid, GET)
    # global option
    if fileid is None:
        if ig_ws is None:
            params['ignorews'] += [1]
            lbl = _('Ignore whitespace')
        ctx_key = 'context'
        ctx_val = ln_ctx
    # per file options
    else:
        if ig_ws is None:
            params[fileid] += ['WS:1']
            lbl = _('Ignore whitespace')

        ctx_key = fileid
        ctx_val = 'C:%s' % ln_ctx
    # if we have passed in ln_ctx pass it along to our params
    if ln_ctx:
        params[ctx_key] += [ctx_val]

    params['anchor'] = fileid
    icon = h.literal('<i class="icon-strike"></i>')
    return h.link_to(icon, h.url.current(**params), title=lbl, **{'data-toggle': 'tooltip'})
Example #26
0
    def delete(self, group_name):
        gr = c.repo_group = RepoGroup.guess_instance(group_name)
        repos = gr.repositories.all()
        if repos:
            h.flash(_('This group contains %s repositories and cannot be '
                      'deleted') % len(repos), category='warning')
            raise HTTPFound(location=url('repos_groups'))

        children = gr.children.all()
        if children:
            h.flash(_('This group contains %s subgroups and cannot be deleted'
                      % (len(children))), category='warning')
            raise HTTPFound(location=url('repos_groups'))

        try:
            RepoGroupModel().delete(group_name)
            Session().commit()
            h.flash(_('Removed repository group %s') % group_name,
                    category='success')
            #TODO: in future action_logger(, '', '', '')
        except Exception:
            log.error(traceback.format_exc())
            h.flash(_('Error occurred during deletion of repository group %s')
                    % group_name, category='error')

        if gr.parent_group:
            raise HTTPFound(location=url('repos_group_home', group_name=gr.parent_group.group_name))
        raise HTTPFound(location=url('repos_groups'))
Example #27
0
 def validate_python(self, value, state):
     super(UniqueUserValidator, self).validate_python(value, state)
     if re.match("^[a-zA-Z0-9_-]*[a-zA-Z_-][a-zA-Z0-9_-]*$", value):
         reg = DBSession.query(User).filter_by(user_name=value).first()
         user = DBSession.query(User).filter_by(user_name=value).first()
         if reg or user:
             raise Invalid(_('Username already in use.'), value, state)
     else:
         raise Invalid(_('Invalid username'), value, state)
Example #28
0
    def delete(self, repo_name, revision, f_path):
        repo = c.db_repo
        if repo.enable_locking and repo.locked[0]:
            h.flash(_('This repository has been locked by %s on %s')
                % (h.person_by_id(repo.locked[0]),
                   h.fmt_date(h.time_to_datetime(repo.locked[1]))),
                'warning')
            raise HTTPFound(location=h.url('files_home',
                                  repo_name=repo_name, revision='tip'))

        # check if revision is a branch identifier- basically we cannot
        # create multiple heads via file editing
        _branches = repo.scm_instance.branches
        # check if revision is a branch name or branch hash
        if revision not in _branches.keys() + _branches.values():
            h.flash(_('You can only delete files with revision '
                      'being a valid branch'), category='warning')
            raise HTTPFound(location=h.url('files_home',
                                  repo_name=repo_name, revision='tip',
                                  f_path=f_path))

        r_post = request.POST

        c.cs = self.__get_cs(revision)
        c.file = self.__get_filenode(c.cs, f_path)

        c.default_message = _('Deleted file %s via Kallithea') % (f_path)
        c.f_path = f_path
        node_path = f_path
        author = request.authuser.full_contact

        if r_post:
            message = r_post.get('message') or c.default_message

            try:
                nodes = {
                    node_path: {
                        'content': ''
                    }
                }
                self.scm_model.delete_nodes(
                    user=request.authuser.user_id, repo=c.db_repo,
                    message=message,
                    nodes=nodes,
                    parent_cs=c.cs,
                    author=author,
                )

                h.flash(_('Successfully deleted file %s') % f_path,
                        category='success')
            except Exception:
                log.error(traceback.format_exc())
                h.flash(_('Error occurred during commit'), category='error')
            raise HTTPFound(location=url('changeset_home',
                                repo_name=c.repo_name, revision='tip'))

        return render('files/files_delete.html')
Example #29
0
 def validate_python(self, value, state):        
     super(UniqueEmailValidator, self).validate_python(value, state)
     if re.match("^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$", value):
         reg = DBSession.query(User).filter_by(email_address=value).first()
         user = DBSession.query(User).filter_by(email_address=value).first()
         if reg or user:
             raise Invalid(_('Email address has already been taken'), value, state)
     else:
         raise Invalid(_('Invalid email'), value, state)
Example #30
0
    def post(
            self,
            name: str,
            email: str,
            password: str,
            is_tracim_manager: str='off',
            is_tracim_admin: str='off',
            send_email: str='off',
    ):
        is_tracim_manager = h.on_off_to_boolean(is_tracim_manager)
        is_tracim_admin = h.on_off_to_boolean(is_tracim_admin)
        send_email = h.on_off_to_boolean(send_email)
        current_user = tmpl_context.current_user

        if current_user.profile.id < Group.TIM_ADMIN:
            # A manager can't give large rights
            is_tracim_manager = False
            is_tracim_admin = False


        api = UserApi(current_user)

        if api.user_with_email_exists(email):
            tg.flash(_('A user with email address "{}" already exists.').format(email), CST.STATUS_ERROR)
            tg.redirect(self.url())

        user = api.create_user()
        user.email = email
        user.display_name = name
        if password:
            user.password = password
        elif send_email:
            # Setup a random password to send email at user
            password = str(uuid.uuid4())
            user.password = password

        user.webdav_left_digest_response_hash = '%s:/:%s' % (email, password)

        api.save(user)

        # Now add the user to related groups
        group_api = GroupApi(current_user)
        user.groups.append(group_api.get_one(Group.TIM_USER))
        if is_tracim_manager:
            user.groups.append(group_api.get_one(Group.TIM_MANAGER))
            if is_tracim_admin:
                user.groups.append(group_api.get_one(Group.TIM_ADMIN))

        api.save(user)

        if send_email:
            email_manager = get_email_manager()
            email_manager.notify_created_account(user, password=password)

        tg.flash(_('User {} created.').format(user.get_display_name()), CST.STATUS_OK)
        tg.redirect(self.url())
Example #31
0
 def allocations(self, *args, **kwargs):
     html = self.get_active_allocations_html(*args, **kwargs)
     javascript = self.get_javascript_allocations_onload()
     title = _("Allocations")
     return dict(title=title, html=html, javascript=javascript)
Example #32
0
    def index(self, repo_name, revision, f_path, annotate=False):
        # redirect to given revision from form if given
        post_revision = request.POST.get('at_rev', None)
        if post_revision:
            cs = self.__get_cs(post_revision)  # FIXME - unused!

        c.revision = revision
        c.changeset = self.__get_cs(revision)
        c.branch = request.GET.get('branch', None)
        c.f_path = f_path
        c.annotate = annotate
        cur_rev = c.changeset.revision
        # used in files_source.html:
        c.cut_off_limit = self.cut_off_limit
        c.fulldiff = request.GET.get('fulldiff')

        # prev link
        try:
            prev_rev = c.db_repo_scm_instance.get_changeset(cur_rev).prev(
                c.branch)
            c.url_prev = url('files_home',
                             repo_name=c.repo_name,
                             revision=prev_rev.raw_id,
                             f_path=f_path)
            if c.branch:
                c.url_prev += '?branch=%s' % c.branch
        except (ChangesetDoesNotExistError, VCSError):
            c.url_prev = '#'

        # next link
        try:
            next_rev = c.db_repo_scm_instance.get_changeset(cur_rev).next(
                c.branch)
            c.url_next = url('files_home',
                             repo_name=c.repo_name,
                             revision=next_rev.raw_id,
                             f_path=f_path)
            if c.branch:
                c.url_next += '?branch=%s' % c.branch
        except (ChangesetDoesNotExistError, VCSError):
            c.url_next = '#'

        # files or dirs
        try:
            c.file = c.changeset.get_node(f_path)

            if c.file.is_submodule():
                raise HTTPFound(location=c.file.url)
            elif c.file.is_file():
                c.load_full_history = False
                # determine if we're on branch head
                _branches = c.db_repo_scm_instance.branches
                c.on_branch_head = revision in _branches or revision in _branches.values(
                )
                _hist = []
                c.file_history = []
                if c.load_full_history:
                    c.file_history, _hist = self._get_node_history(
                        c.changeset, f_path)

                c.authors = []
                for a in set([x.author for x in _hist]):
                    c.authors.append((h.email(a), h.person(a)))
            else:
                c.authors = c.file_history = []
        except RepositoryError as e:
            h.flash(e, category='error')
            raise HTTPNotFound()

        if request.environ.get('HTTP_X_PARTIAL_XHR'):
            return render('files/files_ypjax.html')

        # TODO: tags and bookmarks?
        c.revision_options = [(c.changeset.raw_id,
                              _('%s at %s') % (b, h.short_id(c.changeset.raw_id))) for b in c.changeset.branches] + \
            [(n, b) for b, n in c.db_repo_scm_instance.branches.items()]
        if c.db_repo_scm_instance.closed_branches:
            prefix = _('(closed)') + ' '
            c.revision_options += [('-', '-')] + \
                [(n, prefix + b) for b, n in c.db_repo_scm_instance.closed_branches.items()]

        return render('files/files.html')
Example #33
0
 def title(self):
     return _('Request')
Example #34
0
    def _build_email_body(self, mako_template_filepath: str,
                          role: UserRoleInWorkspace, content: Content,
                          actor: User) -> str:
        """
        Build an email body and return it as a string
        :param mako_template_filepath: the absolute path to the mako template to be used for email body building
        :param role: the role related to user to whom the email must be sent. The role is required (and not the user only) in order to show in the mail why the user receive the notification
        :param content: the content item related to the notification
        :param actor: the user at the origin of the action / notification (for example the one who wrote a comment
        :param config: the global configuration
        :return: the built email body as string. In case of multipart email, this method must be called one time for text and one time for html
        """
        logger.debug(
            self, 'Building email content from MAKO template {}'.format(
                mako_template_filepath))

        template = Template(filename=mako_template_filepath)
        # TODO - D.A. - 2014-11-06 - move this
        # Import is here for circular import problem
        import tracim.lib.helpers as helpers

        dictified_item = Context(
            CTX.EMAIL_NOTIFICATION,
            self._global_config.WEBSITE_BASE_URL).toDict(content)
        dictified_actor = Context(CTX.DEFAULT).toDict(actor)

        main_title = dictified_item.label
        content_intro = ''
        content_text = ''
        call_to_action_text = ''

        action = content.get_last_action().id
        if ActionDescription.COMMENT == action:
            content_intro = _(
                '<span id="content-intro-username">{}</span> added a comment:'
            ).format(actor.display_name)
            content_text = content.description
            call_to_action_text = _('Answer')

        elif ActionDescription.CREATION == action:

            # Default values (if not overriden)
            content_text = content.description
            call_to_action_text = _('View online')

            if ContentType.Thread == content.type:
                call_to_action_text = _('Answer')
                content_intro = _(
                    '<span id="content-intro-username">{}</span> started a thread entitled:'
                ).format(actor.display_name)
                content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + \
                               content.get_last_comment_from(actor).description

            elif ContentType.File == content.type:
                content_intro = _(
                    '<span id="content-intro-username">{}</span> added a file entitled:'
                ).format(actor.display_name)
                if content.description:
                    content_text = content.description
                elif content.label:
                    content_text = '<span id="content-body-only-title">{}</span>'.format(
                        content.label)
                else:
                    content_text = '<span id="content-body-only-title">{}</span>'.format(
                        content.file_name)

            elif ContentType.Page == content.type:
                content_intro = _(
                    '<span id="content-intro-username">{}</span> added a page entitled:'
                ).format(actor.display_name)
                content_text = '<span id="content-body-only-title">{}</span>'.format(
                    content.label)

        elif ActionDescription.REVISION == action:
            content_text = content.description
            call_to_action_text = _('View online')

            if ContentType.File == content.type:
                content_intro = _(
                    '<span id="content-intro-username">{}</span> uploaded a new revision.'
                ).format(actor.display_name)
                content_text = ''

            elif ContentType.Page == content.type:
                content_intro = _(
                    '<span id="content-intro-username">{}</span> updated this page.'
                ).format(actor.display_name)
                previous_revision = content.get_previous_revision()
                title_diff = ''
                if previous_revision.label != content.label:
                    title_diff = htmldiff(previous_revision.label,
                                          content.label)
                content_text = _('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \
                    title_diff + \
                    htmldiff(previous_revision.description, content.description)

            elif ContentType.Thread == content.type:
                content_intro = _(
                    '<span id="content-intro-username">{}</span> updated the thread description.'
                ).format(actor.display_name)
                previous_revision = content.get_previous_revision()
                title_diff = ''
                if previous_revision.label != content.label:
                    title_diff = htmldiff(previous_revision.label,
                                          content.label)
                content_text = _('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \
                    title_diff + \
                    htmldiff(previous_revision.description, content.description)

            # elif ContentType.Thread == content.type:
            #     content_intro = _('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name)
            #     previous_revision = content.get_previous_revision()
            #     content_text = _('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \
            #         htmldiff(previous_revision.description, content.description)

        elif ActionDescription.EDITION == action:
            call_to_action_text = _('View online')

            if ContentType.File == content.type:
                content_intro = _(
                    '<span id="content-intro-username">{}</span> updated the file description.'
                ).format(actor.display_name)
                content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + \
                    content.description

        if '' == content_intro and content_text == '':
            # Skip notification, but it's not normal
            logger.error(
                self, 'A notification is being sent but no content. '
                'Here are some debug informations: [content_id: {cid}]'
                '[action: {act}][author: {actor}]'.format(
                    cid=content.content_id, act=action, actor=actor))
            raise ValueError('Unexpected empty notification')

        # Import done here because cyclic import
        from tracim.config.app_cfg import CFG
        body_content = template.render(
            base_url=self._global_config.WEBSITE_BASE_URL,
            _=_,
            h=helpers,
            user_display_name=role.user.display_name,
            user_role_label=role.role_as_label(),
            workspace_label=role.workspace.label,
            content_intro=content_intro,
            content_text=content_text,
            main_title=main_title,
            call_to_action_text=call_to_action_text,
            result=DictLikeClass(item=dictified_item, actor=dictified_actor),
            CFG=CFG.get_instance(),
        )

        return body_content
Example #35
0
    def add(self, repo_name, revision, f_path):

        repo = c.db_repo
        r_post = request.POST
        c.cs = self.__get_cs(revision, silent_empty=True)
        if c.cs is None:
            c.cs = EmptyChangeset(alias=c.db_repo_scm_instance.alias)
        c.default_message = (_('Added file via Kallithea'))
        c.f_path = f_path

        if r_post:
            unix_mode = 0
            content = convert_line_endings(r_post.get('content', ''),
                                           unix_mode)

            message = r_post.get('message') or c.default_message
            filename = r_post.get('filename')
            location = r_post.get('location', '')
            file_obj = r_post.get('upload_file', None)

            if file_obj is not None and hasattr(file_obj, 'filename'):
                filename = file_obj.filename
                content = file_obj.file

                if hasattr(content, 'file'):
                    # non posix systems store real file under file attr
                    content = content.file

            if not content:
                h.flash(_('No content'), category='warning')
                raise HTTPFound(location=url(
                    'changeset_home', repo_name=c.repo_name, revision='tip'))
            if not filename:
                h.flash(_('No filename'), category='warning')
                raise HTTPFound(location=url(
                    'changeset_home', repo_name=c.repo_name, revision='tip'))
            # strip all crap out of file, just leave the basename
            filename = os.path.basename(filename)
            node_path = posixpath.join(location, filename)
            author = request.authuser.full_contact

            try:
                nodes = {node_path: {'content': content}}
                self.scm_model.create_nodes(
                    user=request.authuser.user_id,
                    ip_addr=request.ip_addr,
                    repo=c.db_repo,
                    message=message,
                    nodes=nodes,
                    parent_cs=c.cs,
                    author=author,
                )

                h.flash(_('Successfully committed to %s') % node_path,
                        category='success')
            except NonRelativePathError as e:
                h.flash(_('Location must be relative path and must not '
                          'contain .. in path'),
                        category='warning')
                raise HTTPFound(location=url(
                    'changeset_home', repo_name=c.repo_name, revision='tip'))
            except (NodeError, NodeAlreadyExistsError) as e:
                h.flash(_(e), category='error')
            except Exception:
                log.error(traceback.format_exc())
                h.flash(_('Error occurred during commit'), category='error')
            raise HTTPFound(location=url(
                'changeset_home', repo_name=c.repo_name, revision='tip'))

        return render('files/files_add.html')
Example #36
0
    def save(self, **kw):
        kw.pop('nothing')
        flash_message = _('Profile successfully updated')
        user = request.identity['user']

        # validate the form
        try:
            form = create_user_form(user)
            kw = form.validate(kw)
        except ValidationError:
            override_template(self.save, 'kajiki:userprofile.templates.edit')
            user_data, user_avatar = get_user_data(user)
            if sys.version_info >= (3, 3):
                from types import SimpleNamespace
            else:
                from argparse import Namespace as SimpleNamespace
            u = SimpleNamespace()
            for k, v in kw.items():
                u.__setattr__(k, v)
            return dict(user=u,
                        profile_css=get_profile_css(config),
                        user_avatar=user_avatar,
                        form=form)

        # get the profile_save function that may be custom
        profile_save = getattr(user, 'save_profile', None)
        if not profile_save:
            profile_save = update_user_data

        # we don't want to save the email until it is confirmed
        new_email = kw['email_address']
        kw['email_address'] = user.email_address
        profile_save(user, kw)

        if new_email != user.email_address:
            # save this new email in the db
            dictionary = {
                'old_email_address': user.email_address,
                'email_address': new_email,
                'activation_code':
                    model.ProfileActivation.generate_activation_code(new_email),
            }
            activation = model.provider.create(model.ProfileActivation, dictionary)

            # ok, send the email please
            userprofile_config = config.get('_pluggable_userprofile_config')
            mail_body = userprofile_config.get(
                'mail_body',
                _('Please click on this link to confermate your email address')
                + '\n\n' + activation.activation_link,
            )
            email_data = {'sender': config['userprofile.email_sender'],
                          'subject': userprofile_config.get(
                              'mail_subject', _('Please confirm your email')),
                          'body': mail_body,
                          'rich': userprofile_config.get('mail_rich', '')}
            send_email(new_email, **email_data)
            flash_message += '.\n' + _('Confirm your email please')

        flash(flash_message)
        return redirect(plug_url('userprofile', '/'))
Example #37
0
    def _index(self, revision, method):
        c.pull_request = None
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = request.GET.get(
            'fulldiff')  # for reporting number of changed files
        # get ranges of revisions if preset
        rev_range = revision.split('...')[:2]
        enable_comments = True
        c.cs_repo = c.db_repo
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.db_repo_scm_instance.get_changesets(
                    start=rev_start, end=rev_end)
            else:
                rev_ranges = [c.db_repo_scm_instance.get_changeset(revision)]

            c.cs_ranges = list(rev_ranges)
            if not c.cs_ranges:
                raise RepositoryError('Changeset range returned empty result')

        except (ChangesetDoesNotExistError, EmptyRepositoryError):
            log.debug(traceback.format_exc())
            msg = _('Such revision does not exist for this repository')
            h.flash(msg, category='error')
            raise HTTPNotFound()

        c.changes = OrderedDict()

        c.lines_added = 0  # count of lines added
        c.lines_deleted = 0  # count of lines removes

        c.changeset_statuses = ChangesetStatus.STATUSES
        comments = dict()
        c.statuses = []
        c.inline_comments = []
        c.inline_cnt = 0

        # Iterate over ranges (default changeset view is always one changeset)
        for changeset in c.cs_ranges:
            if method == 'show':
                c.statuses.extend([
                    ChangesetStatusModel().get_status(c.db_repo.repo_id,
                                                      changeset.raw_id)
                ])

                # Changeset comments
                comments.update(
                    (com.comment_id, com)
                    for com in ChangesetCommentsModel().get_comments(
                        c.db_repo.repo_id, revision=changeset.raw_id))

                # Status change comments - mostly from pull requests
                comments.update((
                    st.comment_id, st.comment
                ) for st in ChangesetStatusModel().get_statuses(
                    c.db_repo.repo_id, changeset.raw_id, with_revisions=True)
                                if st.comment_id is not None)

                inlines = ChangesetCommentsModel() \
                            .get_inline_comments(c.db_repo.repo_id,
                                                 revision=changeset.raw_id)
                c.inline_comments.extend(inlines)

            cs2 = changeset.raw_id
            cs1 = changeset.parents[
                0].raw_id if changeset.parents else EmptyChangeset().raw_id
            context_lcl = get_line_ctx('', request.GET)
            ign_whitespace_lcl = get_ignore_ws('', request.GET)

            raw_diff = diffs.get_diff(c.db_repo_scm_instance,
                                      cs1,
                                      cs2,
                                      ignore_whitespace=ign_whitespace_lcl,
                                      context=context_lcl)
            diff_limit = None if c.fulldiff else self.cut_off_limit
            file_diff_data = []
            if method == 'show':
                diff_processor = diffs.DiffProcessor(
                    raw_diff,
                    vcs=c.db_repo_scm_instance.alias,
                    diff_limit=diff_limit)
                c.limited_diff = diff_processor.limited_diff
                for f in diff_processor.parsed:
                    st = f['stats']
                    c.lines_added += st['added']
                    c.lines_deleted += st['deleted']
                    filename = f['filename']
                    fid = h.FID(changeset.raw_id, filename)
                    url_fid = h.FID('', filename)
                    html_diff = diffs.as_html(enable_comments=enable_comments,
                                              parsed_lines=[f])
                    file_diff_data.append(
                        (fid, url_fid, f['operation'], f['old_filename'],
                         filename, html_diff, st))
            else:
                # downloads/raw we only need RAW diff nothing else
                file_diff_data.append(('', None, None, None, raw_diff, None))
            c.changes[changeset.raw_id] = (cs1, cs2, file_diff_data)

        # sort comments in creation order
        c.comments = [com for com_id, com in sorted(comments.items())]

        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.cs_ranges) == 1:
            c.changeset = c.cs_ranges[0]
            c.parent_tmpl = ''.join(
                ['# Parent  %s\n' % x.raw_id for x in c.changeset.parents])
            c.changeset_graft_source_hash = ascii_str(
                c.changeset.extra.get(b'source', b''))
            c.changeset_transplant_source_hash = ascii_str(
                binascii.hexlify(
                    c.changeset.extra.get(b'transplant_source', b'')))
        if method == 'download':
            response.content_type = 'text/plain'
            response.content_disposition = 'attachment; filename=%s.diff' \
                                            % revision[:12]
            return raw_diff
        elif method == 'patch':
            response.content_type = 'text/plain'
            c.diff = safe_str(raw_diff)
            return render('changeset/patch_changeset.html')
        elif method == 'raw':
            response.content_type = 'text/plain'
            return raw_diff
        elif method == 'show':
            if len(c.cs_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                c.cs_ranges_org = None
                c.cs_comments = {}
                revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
                c.jsdata = graph_data(c.db_repo_scm_instance, revs)
                return render('changeset/changeset_range.html')
Example #38
0
def serialize_content_for_folder_content_list(content: Content,
                                              context: Context):
    content_type = ContentType(content.type)

    last_activity_date = content.get_last_activity_date()
    last_activity_date_formatted = format_datetime(
        last_activity_date, locale=tg.i18n.get_lang()[0])
    last_activity_label = format_timedelta(datetime.utcnow() -
                                           last_activity_date,
                                           locale=tg.i18n.get_lang()[0])
    last_activity_label = last_activity_label.replace(
        ' ', '\u00A0')  # espace insécable

    item = None
    if ContentType.Thread == content.type:
        item = Context(CTX.THREADS).toDict(content)
        item.type = context.toDict(content_type)
        item.folder = DictLikeClass({'id': content.parent_id
                                     }) if content.parent else None
        item.workspace = DictLikeClass({'id': content.workspace.workspace_id
                                        }) if content.workspace else None
        item.last_activity = DictLikeClass({
            'date': last_activity_date,
            'label': last_activity_date_formatted,
            'delta': last_activity_label
        })

        comments = content.get_comments()
        if len(comments) > 1:
            item.notes = _('{nb} messages').format(nb=len(comments))
        else:
            item.notes = _('1 message')

    elif ContentType.File == content.type:
        item = Context(CTX.CONTENT_LIST).toDict(content)
        if len(content.revisions) > 1:
            item.notes = _('{nb} revisions').format(nb=len(content.revisions))
        else:
            item.notes = _('1 revision')

    elif ContentType.Folder == content.type:
        item = Context(CTX.CONTENT_LIST).toDict(content)
        item.notes = ''

        folder_nb = content.get_child_nb(ContentType.Folder)
        if folder_nb == 1:
            item.notes += _('1 subfolder<br/>\n')
        elif folder_nb > 1:
            item.notes += _('{} subfolders<br/>').format(folder_nb)

        file_nb = content.get_child_nb(ContentType.File, ContentStatus.OPEN)
        if file_nb == 1:
            item.notes += _('1 open file<br/>\n')
        elif file_nb > 1:
            item.notes += _('{} open files<br/>').format(file_nb)

        thread_nb = content.get_child_nb(ContentType.Thread,
                                         ContentStatus.OPEN)
        if thread_nb == 1:
            item.notes += _('1 open thread<br/>\n')
        elif thread_nb > 1:
            item.notes += _('{} open threads<br/>').format(thread_nb)

        page_nb = content.get_child_nb(ContentType.Page, ContentStatus.OPEN)
        if page_nb == 1:
            item.notes += _('1 open page<br/>\n')
        elif page_nb > 1:
            item.notes += _('{} open pages<br/>').format(page_nb)
    else:
        item = Context(CTX.CONTENT_LIST).toDict(content)
        item.notes = ''

    return item
Example #39
0
 def policy_sales(self, *args, **kwargs):
     html = self.get_active_policy_sales_html(*args, **kwargs)
     javascript = self.get_javascript_policy_sales_onload()
     title = _("Policy Sales")
     return dict(title=title, html=html, javascript=javascript)
Example #40
0
 def post_logout(self, came_from=lurl('/')):
     """
     Redirect the user to the initially requested page on logout and say  goodbye as well.
     """
     flash(_('Successfully logged out. We hope to see you soon!'))
     redirect(came_from)
Example #41
0
    def put(self, new_profile):
        # FIXME - Allow only self password or operation for managers
        current_user = tmpl_context.current_user
        user = tmpl_context.user

        group_api = GroupApi(current_user)

        if current_user.user_id == user.user_id:
            tg.flash(_('You can\'t change your own profile'), CST.STATUS_ERROR)
            tg.redirect(self.parent_controller.url())

        redirect_url = self.parent_controller.url(skip_id=True)

        if new_profile not in self.allowed_profiles:
            tg.flash(_('Unknown profile'), CST.STATUS_ERROR)
            tg.redirect(redirect_url)

        pod_user_group = group_api.get_one(Group.TIM_USER)
        pod_manager_group = group_api.get_one(Group.TIM_MANAGER)
        pod_admin_group = group_api.get_one(Group.TIM_ADMIN)

        flash_message = _(
            'User updated.')  # this is the default value ; should never appear

        if new_profile == UserProfileAdminRestController._ALLOWED_PROFILE_USER:
            if pod_user_group not in user.groups:
                user.groups.append(pod_user_group)

            try:
                user.groups.remove(pod_manager_group)
            except:
                pass

            try:
                user.groups.remove(pod_admin_group)
            except:
                pass

            flash_message = _('User {} is now a basic user').format(
                user.get_display_name())

        elif new_profile == UserProfileAdminRestController._ALLOWED_PROFILE_MANAGER:
            if pod_user_group not in user.groups:
                user.groups.append(pod_user_group)
            if pod_manager_group not in user.groups:
                user.groups.append(pod_manager_group)

            try:
                user.groups.remove(pod_admin_group)
            except:
                pass

            flash_message = _('User {} can now workspaces').format(
                user.get_display_name())

        elif new_profile == UserProfileAdminRestController._ALLOWED_PROFILE_ADMIN:
            if pod_user_group not in user.groups:
                user.groups.append(pod_user_group)
            if pod_manager_group not in user.groups:
                user.groups.append(pod_manager_group)
            if pod_admin_group not in user.groups:
                user.groups.append(pod_admin_group)

            flash_message = _('User {} is now an administrator').format(
                user.get_display_name())

        else:
            logger.error(
                self,
                'Trying to change user {} profile with unexpected profile {}'.
                format(user.user_id, new_profile))
            tg.flash(_('Unknown profile'), CST.STATUS_ERROR)
            tg.redirect(redirect_url)

        DBSession.flush()
        tg.flash(flash_message, CST.STATUS_OK)
        tg.redirect(redirect_url)
Example #42
0
 def glaccounts(self, *args, **kwargs):
     html = self.get_active_glaccounts_html(*args, **kwargs)
     javascript = self.get_javascript_glaccounts_onload()
     title = _("GL Account Extract")
     return dict(title=title, html=html, javascript=javascript)
Example #43
0
    def add_reviewers(self, user, pr, reviewers, mention_recipients=None):
        """Add reviewer and send notification to them.
        """
        reviewers = set(reviewers)
        _assert_valid_reviewers(reviewers)
        if mention_recipients is not None:
            mention_recipients = set(mention_recipients) - reviewers
            _assert_valid_reviewers(mention_recipients)

        # members
        for reviewer in reviewers:
            prr = PullRequestReviewer(reviewer, pr)
            Session().add(prr)

        # notification to reviewers
        pr_url = pr.url(canonical=True)
        threading = ['%s-pr-%s@%s' % (pr.other_repo.repo_name,
                                      pr.pull_request_id,
                                      h.canonical_hostname())]
        subject = h.link_to(
            _('%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s') %
                {'user': user.username,
                 'pr_title': pr.title,
                 'pr_nice_id': pr.nice_id()},
            pr_url)
        body = pr.description
        _org_ref_type, org_ref_name, _org_rev = pr.org_ref.split(':')
        _other_ref_type, other_ref_name, _other_rev = pr.other_ref.split(':')
        revision_data = [(x.raw_id, x.message)
                         for x in map(pr.org_repo.get_changeset, pr.revisions)]
        email_kwargs = {
            'pr_title': pr.title,
            'pr_title_short': h.shorter(pr.title, 50),
            'pr_user_created': user.full_name_and_username,
            'pr_repo_url': h.canonical_url('summary_home', repo_name=pr.other_repo.repo_name),
            'pr_url': pr_url,
            'pr_revisions': revision_data,
            'repo_name': pr.other_repo.repo_name,
            'org_repo_name': pr.org_repo.repo_name,
            'pr_nice_id': pr.nice_id(),
            'pr_target_repo': h.canonical_url('summary_home',
                               repo_name=pr.other_repo.repo_name),
            'pr_target_branch': other_ref_name,
            'pr_source_repo': h.canonical_url('summary_home',
                               repo_name=pr.org_repo.repo_name),
            'pr_source_branch': org_ref_name,
            'pr_owner': pr.owner,
            'pr_owner_username': pr.owner.username,
            'pr_username': user.username,
            'threading': threading,
            'is_mention': False,
            }
        if reviewers:
            NotificationModel().create(created_by=user, subject=subject, body=body,
                                       recipients=reviewers,
                                       type_=NotificationModel.TYPE_PULL_REQUEST,
                                       email_kwargs=email_kwargs)

        if mention_recipients:
            email_kwargs['is_mention'] = True
            subject = _('[Mention]') + ' ' + subject
            # FIXME: this subject is wrong and unused!
            NotificationModel().create(created_by=user, subject=subject, body=body,
                                       recipients=mention_recipients,
                                       type_=NotificationModel.TYPE_PULL_REQUEST,
                                       email_kwargs=email_kwargs)
Example #44
0
def not_mapped_error(repo_name):
    flash(_('%s repository is not mapped to db perhaps'
            ' it was created or renamed from the filesystem'
            ' please run the application again'
            ' in order to rescan repositories') % repo_name,
          category='error')
Example #45
0
 def index(self):
     """Let the user know that's visiting a protected controller."""
     flash(_("Secure Controller here"))
     return dict(page='index')
Example #46
0
def action_parser(user_log, feed=False, parse_cs=False):
    """
    This helper will action_map the specified string action into translated
    fancy names with icons and links

    :param user_log: user log instance
    :param feed: use output for feeds (no html and fancy icons)
    :param parse_cs: parse Changesets into VCS instances
    """

    action = user_log.action
    action_params = ' '

    x = action.split(':')

    if len(x) > 1:
        action, action_params = x

    def get_cs_links():
        revs_limit = 3  # display this amount always
        revs_top_limit = 50  # show upto this amount of changesets hidden
        revs_ids = action_params.split(',')
        deleted = user_log.repository is None
        if deleted:
            return ','.join(revs_ids)

        repo_name = user_log.repository.repo_name

        def lnk(rev, repo_name):
            lazy_cs = False
            title_ = None
            url_ = '#'
            if isinstance(rev, BaseChangeset) or isinstance(
                    rev, AttributeDict):
                if rev.op and rev.ref_name:
                    if rev.op == 'delete_branch':
                        lbl = _('Deleted branch: %s') % rev.ref_name
                    elif rev.op == 'tag':
                        lbl = _('Created tag: %s') % rev.ref_name
                    else:
                        lbl = 'Unknown operation %s' % rev.op
                else:
                    lazy_cs = True
                    lbl = rev.short_id[:8]
                    url_ = url('changeset_home',
                               repo_name=repo_name,
                               revision=rev.raw_id)
            else:
                # changeset cannot be found - it might have been stripped or removed
                lbl = rev[:12]
                title_ = _('Changeset %s not found') % lbl
            if parse_cs:
                return link_to(lbl,
                               url_,
                               title=title_,
                               **{'data-toggle': 'tooltip'})
            return link_to(lbl,
                           url_,
                           class_='lazy-cs' if lazy_cs else '',
                           **{
                               'data-raw_id': rev.raw_id,
                               'data-repo_name': repo_name
                           })

        def _get_op(rev_txt):
            _op = None
            _name = rev_txt
            if len(rev_txt.split('=>')) == 2:
                _op, _name = rev_txt.split('=>')
            return _op, _name

        revs = []
        if len(filter(lambda v: v != '', revs_ids)) > 0:
            repo = None
            for rev in revs_ids[:revs_top_limit]:
                _op, _name = _get_op(rev)

                # we want parsed changesets, or new log store format is bad
                if parse_cs:
                    try:
                        if repo is None:
                            repo = user_log.repository.scm_instance
                        _rev = repo.get_changeset(rev)
                        revs.append(_rev)
                    except ChangesetDoesNotExistError:
                        log.error('cannot find revision %s in this repo', rev)
                        revs.append(rev)
                else:
                    _rev = AttributeDict({
                        'short_id': rev[:12],
                        'raw_id': rev,
                        'message': '',
                        'op': _op,
                        'ref_name': _name
                    })
                    revs.append(_rev)
        cs_links = [
            " " + ', '.join([lnk(rev, repo_name) for rev in revs[:revs_limit]])
        ]
        _op1, _name1 = _get_op(revs_ids[0])
        _op2, _name2 = _get_op(revs_ids[-1])

        _rev = '%s...%s' % (_name1, _name2)

        compare_view = (
            ' <div class="compare_view" data-toggle="tooltip" title="%s">'
            '<a href="%s">%s</a> </div>' %
            (_('Show all combined changesets %s->%s') %
             (revs_ids[0][:12], revs_ids[-1][:12]),
             url('changeset_home', repo_name=repo_name,
                 revision=_rev), _('Compare view')))

        # if we have exactly one more than normally displayed
        # just display it, takes less space than displaying
        # "and 1 more revisions"
        if len(revs_ids) == revs_limit + 1:
            cs_links.append(", " + lnk(revs[revs_limit], repo_name))

        # hidden-by-default ones
        if len(revs_ids) > revs_limit + 1:
            uniq_id = revs_ids[0]
            html_tmpl = ('<span> %s <a class="show_more" id="_%s" '
                         'href="#more">%s</a> %s</span>')
            if not feed:
                cs_links.append(html_tmpl %
                                (_('and'), uniq_id, _('%s more') %
                                 (len(revs_ids) - revs_limit), _('revisions')))

            if not feed:
                html_tmpl = '<span id="%s" style="display:none">, %s </span>'
            else:
                html_tmpl = '<span id="%s"> %s </span>'

            morelinks = ', '.join(
                [lnk(rev, repo_name) for rev in revs[revs_limit:]])

            if len(revs_ids) > revs_top_limit:
                morelinks += ', ...'

            cs_links.append(html_tmpl % (uniq_id, morelinks))
        if len(revs) > 1:
            cs_links.append(compare_view)
        return ''.join(cs_links)

    def get_fork_name():
        repo_name = action_params
        url_ = url('summary_home', repo_name=repo_name)
        return _('Fork name %s') % link_to(action_params, url_)

    def get_user_name():
        user_name = action_params
        return user_name

    def get_users_group():
        group_name = action_params
        return group_name

    def get_pull_request():
        from kallithea.model.db import PullRequest
        pull_request_id = action_params
        nice_id = PullRequest.make_nice_id(pull_request_id)

        deleted = user_log.repository is None
        if deleted:
            repo_name = user_log.repository_name
        else:
            repo_name = user_log.repository.repo_name

        return link_to(
            _('Pull request %s') % nice_id,
            url('pullrequest_show',
                repo_name=repo_name,
                pull_request_id=pull_request_id))

    def get_archive_name():
        archive_name = action_params
        return archive_name

    # action : translated str, callback(extractor), icon
    action_map = {
        'user_deleted_repo':
        (_('[deleted] repository'), None, 'icon-trashcan'),
        'user_created_repo': (_('[created] repository'), None, 'icon-plus'),
        'user_created_fork':
        (_('[created] repository as fork'), None, 'icon-fork'),
        'user_forked_repo':
        (_('[forked] repository'), get_fork_name, 'icon-fork'),
        'user_updated_repo': (_('[updated] repository'), None, 'icon-pencil'),
        'user_downloaded_archive': (_('[downloaded] archive from repository'),
                                    get_archive_name, 'icon-download-cloud'),
        'admin_deleted_repo':
        (_('[delete] repository'), None, 'icon-trashcan'),
        'admin_created_repo': (_('[created] repository'), None, 'icon-plus'),
        'admin_forked_repo': (_('[forked] repository'), None, 'icon-fork'),
        'admin_updated_repo': (_('[updated] repository'), None, 'icon-pencil'),
        'admin_created_user': (_('[created] user'), get_user_name,
                               'icon-user'),
        'admin_updated_user': (_('[updated] user'), get_user_name,
                               'icon-user'),
        'admin_created_users_group': (_('[created] user group'),
                                      get_users_group, 'icon-pencil'),
        'admin_updated_users_group': (_('[updated] user group'),
                                      get_users_group, 'icon-pencil'),
        'user_commented_revision': (_('[commented] on revision in repository'),
                                    get_cs_links, 'icon-comment'),
        'user_commented_pull_request': (_('[commented] on pull request for'),
                                        get_pull_request, 'icon-comment'),
        'user_closed_pull_request': (_('[closed] pull request for'),
                                     get_pull_request, 'icon-ok'),
        'push': (_('[pushed] into'), get_cs_links, 'icon-move-up'),
        'push_local': (_('[committed via Kallithea] into repository'),
                       get_cs_links, 'icon-pencil'),
        'push_remote': (_('[pulled from remote] into repository'),
                        get_cs_links, 'icon-move-up'),
        'pull': (_('[pulled] from'), None, 'icon-move-down'),
        'started_following_repo': (_('[started following] repository'), None,
                                   'icon-heart'),
        'stopped_following_repo': (_('[stopped following] repository'), None,
                                   'icon-heart-empty'),
    }

    action_str = action_map.get(action, action)
    if feed:
        action = action_str[0].replace('[', '').replace(']', '')
    else:
        action = action_str[0] \
            .replace('[', '<b>') \
            .replace(']', '</b>')

    action_params_func = lambda: ""

    if callable(action_str[1]):
        action_params_func = action_str[1]

    def action_parser_icon():
        action = user_log.action
        action_params = None
        x = action.split(':')

        if len(x) > 1:
            action, action_params = x

        ico = action_map.get(action, ['', '', ''])[2]
        html = """<i class="%s"></i>""" % ico
        return literal(html)

    # returned callbacks we need to call to get
    return [lambda: literal(action), action_params_func, action_parser_icon]
Example #47
0
    def archivefile(self, repo_name, fname):
        fileformat = None
        revision = None
        ext = None
        subrepos = request.GET.get('subrepos') == 'true'

        for a_type, ext_data in settings.ARCHIVE_SPECS.items():
            archive_spec = fname.split(ext_data[1])
            if len(archive_spec) == 2 and archive_spec[1] == '':
                fileformat = a_type or ext_data[1]
                revision = archive_spec[0]
                ext = ext_data[1]

        try:
            dbrepo = RepoModel().get_by_repo_name(repo_name)
            if not dbrepo.enable_downloads:
                return _('Downloads disabled')  # TODO: do something else?

            if c.db_repo_scm_instance.alias == 'hg':
                # patch and reset hooks section of UI config to not run any
                # hooks on fetching archives with subrepos
                for k, v in c.db_repo_scm_instance._repo.ui.configitems(
                        'hooks'):
                    c.db_repo_scm_instance._repo.ui.setconfig('hooks', k, None)

            cs = c.db_repo_scm_instance.get_changeset(revision)
            content_type = settings.ARCHIVE_SPECS[fileformat][0]
        except ChangesetDoesNotExistError:
            return _('Unknown revision %s') % revision
        except EmptyRepositoryError:
            return _('Empty repository')
        except (ImproperArchiveTypeError, KeyError):
            return _('Unknown archive type')

        from kallithea import CONFIG
        rev_name = cs.raw_id[:12]
        archive_name = '%s-%s%s' % (repo_name.replace('/', '_'), rev_name, ext)

        archive_path = None
        cached_archive_path = None
        archive_cache_dir = CONFIG.get('archive_cache_dir')
        if archive_cache_dir and not subrepos:  # TODO: subrepo caching?
            if not os.path.isdir(archive_cache_dir):
                os.makedirs(archive_cache_dir)
            cached_archive_path = os.path.join(archive_cache_dir, archive_name)
            if os.path.isfile(cached_archive_path):
                log.debug('Found cached archive in %s', cached_archive_path)
                archive_path = cached_archive_path
            else:
                log.debug('Archive %s is not yet cached', archive_name)

        if archive_path is None:
            # generate new archive
            fd, archive_path = tempfile.mkstemp()
            log.debug('Creating new temp archive in %s', archive_path)
            with os.fdopen(fd, 'wb') as stream:
                cs.fill_archive(stream=stream,
                                kind=fileformat,
                                subrepos=subrepos)
                # stream (and thus fd) has been closed by cs.fill_archive
            if cached_archive_path is not None:
                # we generated the archive - move it to cache
                log.debug('Storing new archive in %s', cached_archive_path)
                shutil.move(archive_path, cached_archive_path)
                archive_path = cached_archive_path

        def get_chunked_archive(archive_path):
            stream = open(archive_path, 'rb')
            while True:
                data = stream.read(16 * 1024)
                if not data:
                    break
                yield data
            stream.close()
            if archive_path != cached_archive_path:
                log.debug('Destroying temp archive %s', archive_path)
                os.remove(archive_path)

        action_logger(user=request.authuser,
                      action='user_downloaded_archive:%s' % (archive_name),
                      repo=repo_name,
                      ipaddr=request.ip_addr,
                      commit=True)

        response.content_disposition = str('attachment; filename=%s' %
                                           (archive_name))
        response.content_type = str(content_type)
        return get_chunked_archive(archive_path)
Example #48
0
    def get_cs_links():
        revs_limit = 3  # display this amount always
        revs_top_limit = 50  # show upto this amount of changesets hidden
        revs_ids = action_params.split(',')
        deleted = user_log.repository is None
        if deleted:
            return ','.join(revs_ids)

        repo_name = user_log.repository.repo_name

        def lnk(rev, repo_name):
            lazy_cs = False
            title_ = None
            url_ = '#'
            if isinstance(rev, BaseChangeset) or isinstance(
                    rev, AttributeDict):
                if rev.op and rev.ref_name:
                    if rev.op == 'delete_branch':
                        lbl = _('Deleted branch: %s') % rev.ref_name
                    elif rev.op == 'tag':
                        lbl = _('Created tag: %s') % rev.ref_name
                    else:
                        lbl = 'Unknown operation %s' % rev.op
                else:
                    lazy_cs = True
                    lbl = rev.short_id[:8]
                    url_ = url('changeset_home',
                               repo_name=repo_name,
                               revision=rev.raw_id)
            else:
                # changeset cannot be found - it might have been stripped or removed
                lbl = rev[:12]
                title_ = _('Changeset %s not found') % lbl
            if parse_cs:
                return link_to(lbl,
                               url_,
                               title=title_,
                               **{'data-toggle': 'tooltip'})
            return link_to(lbl,
                           url_,
                           class_='lazy-cs' if lazy_cs else '',
                           **{
                               'data-raw_id': rev.raw_id,
                               'data-repo_name': repo_name
                           })

        def _get_op(rev_txt):
            _op = None
            _name = rev_txt
            if len(rev_txt.split('=>')) == 2:
                _op, _name = rev_txt.split('=>')
            return _op, _name

        revs = []
        if len(filter(lambda v: v != '', revs_ids)) > 0:
            repo = None
            for rev in revs_ids[:revs_top_limit]:
                _op, _name = _get_op(rev)

                # we want parsed changesets, or new log store format is bad
                if parse_cs:
                    try:
                        if repo is None:
                            repo = user_log.repository.scm_instance
                        _rev = repo.get_changeset(rev)
                        revs.append(_rev)
                    except ChangesetDoesNotExistError:
                        log.error('cannot find revision %s in this repo', rev)
                        revs.append(rev)
                else:
                    _rev = AttributeDict({
                        'short_id': rev[:12],
                        'raw_id': rev,
                        'message': '',
                        'op': _op,
                        'ref_name': _name
                    })
                    revs.append(_rev)
        cs_links = [
            " " + ', '.join([lnk(rev, repo_name) for rev in revs[:revs_limit]])
        ]
        _op1, _name1 = _get_op(revs_ids[0])
        _op2, _name2 = _get_op(revs_ids[-1])

        _rev = '%s...%s' % (_name1, _name2)

        compare_view = (
            ' <div class="compare_view" data-toggle="tooltip" title="%s">'
            '<a href="%s">%s</a> </div>' %
            (_('Show all combined changesets %s->%s') %
             (revs_ids[0][:12], revs_ids[-1][:12]),
             url('changeset_home', repo_name=repo_name,
                 revision=_rev), _('Compare view')))

        # if we have exactly one more than normally displayed
        # just display it, takes less space than displaying
        # "and 1 more revisions"
        if len(revs_ids) == revs_limit + 1:
            cs_links.append(", " + lnk(revs[revs_limit], repo_name))

        # hidden-by-default ones
        if len(revs_ids) > revs_limit + 1:
            uniq_id = revs_ids[0]
            html_tmpl = ('<span> %s <a class="show_more" id="_%s" '
                         'href="#more">%s</a> %s</span>')
            if not feed:
                cs_links.append(html_tmpl %
                                (_('and'), uniq_id, _('%s more') %
                                 (len(revs_ids) - revs_limit), _('revisions')))

            if not feed:
                html_tmpl = '<span id="%s" style="display:none">, %s </span>'
            else:
                html_tmpl = '<span id="%s"> %s </span>'

            morelinks = ', '.join(
                [lnk(rev, repo_name) for rev in revs[revs_limit:]])

            if len(revs_ids) > revs_top_limit:
                morelinks += ', ...'

            cs_links.append(html_tmpl % (uniq_id, morelinks))
        if len(revs) > 1:
            cs_links.append(compare_view)
        return ''.join(cs_links)
Example #49
0
    def edit(self, repo_name, revision, f_path):
        repo = c.db_repo
        # check if revision is a branch identifier- basically we cannot
        # create multiple heads via file editing
        _branches = repo.scm_instance.branches
        # check if revision is a branch name or branch hash
        if revision not in _branches and revision not in _branches.values():
            h.flash(_('You can only edit files with revision '
                      'being a valid branch'),
                    category='warning')
            raise HTTPFound(location=h.url('files_home',
                                           repo_name=repo_name,
                                           revision='tip',
                                           f_path=f_path))

        r_post = request.POST

        c.cs = self.__get_cs(revision)
        c.file = self.__get_filenode(c.cs, f_path)

        if c.file.is_binary:
            raise HTTPFound(location=url('files_home',
                                         repo_name=c.repo_name,
                                         revision=c.cs.raw_id,
                                         f_path=f_path))
        c.default_message = _('Edited file %s via Kallithea') % (f_path)
        c.f_path = f_path

        if r_post:
            old_content = safe_str(c.file.content)
            sl = old_content.splitlines(1)
            first_line = sl[0] if sl else ''
            # modes:  0 - Unix, 1 - Mac, 2 - DOS
            mode = detect_mode(first_line, 0)
            content = convert_line_endings(r_post.get('content', ''), mode)

            message = r_post.get('message') or c.default_message
            author = request.authuser.full_contact

            if content == old_content:
                h.flash(_('No changes'), category='warning')
                raise HTTPFound(location=url(
                    'changeset_home', repo_name=c.repo_name, revision='tip'))
            try:
                self.scm_model.commit_change(repo=c.db_repo_scm_instance,
                                             repo_name=repo_name,
                                             cs=c.cs,
                                             user=request.authuser.user_id,
                                             ip_addr=request.ip_addr,
                                             author=author,
                                             message=message,
                                             content=content,
                                             f_path=f_path)
                h.flash(_('Successfully committed to %s') % f_path,
                        category='success')
            except Exception:
                log.error(traceback.format_exc())
                h.flash(_('Error occurred during commit'), category='error')
            raise HTTPFound(location=url(
                'changeset_home', repo_name=c.repo_name, revision='tip'))

        return render('files/files_edit.html')
Example #50
0
 def get_fork_name():
     repo_name = action_params
     url_ = url('summary_home', repo_name=repo_name)
     return _('Fork name %s') % link_to(action_params, url_)
Example #51
0
 def title(self):
     return _('Logging')
Example #52
0
def fancy_file_stats(stats):
    """
    Displays a fancy two colored bar for number of added/deleted
    lines of code on file

    :param stats: two element list of added/deleted lines of code
    """
    from kallithea.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
        MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE

    a, d = stats['added'], stats['deleted']
    width = 100

    if stats['binary']:
        #binary mode
        lbl = ''
        bin_op = 1

        if BIN_FILENODE in stats['ops']:
            lbl = 'bin+'

        if NEW_FILENODE in stats['ops']:
            lbl += _('new file')
            bin_op = NEW_FILENODE
        elif MOD_FILENODE in stats['ops']:
            lbl += _('mod')
            bin_op = MOD_FILENODE
        elif DEL_FILENODE in stats['ops']:
            lbl += _('del')
            bin_op = DEL_FILENODE
        elif RENAMED_FILENODE in stats['ops']:
            lbl += _('rename')
            bin_op = RENAMED_FILENODE

        #chmod can go with other operations
        if CHMOD_FILENODE in stats['ops']:
            _org_lbl = _('chmod')
            lbl += _org_lbl if lbl.endswith('+') else '+%s' % _org_lbl

        #import ipdb;ipdb.set_trace()
        b_d = '<div class="bin bin%s progress-bar" style="width:100%%">%s</div>' % (
            bin_op, lbl)
        b_a = '<div class="bin bin1" style="width:0%"></div>'
        return literal('<div style="width:%spx" class="progress">%s%s</div>' %
                       (width, b_a, b_d))

    t = stats['added'] + stats['deleted']
    unit = float(width) / (t or 1)

    # needs > 9% of width to be visible or 0 to be hidden
    a_p = max(9, unit * a) if a > 0 else 0
    d_p = max(9, unit * d) if d > 0 else 0
    p_sum = a_p + d_p

    if p_sum > width:
        #adjust the percentage to be == 100% since we adjusted to 9
        if a_p > d_p:
            a_p = a_p - (p_sum - width)
        else:
            d_p = d_p - (p_sum - width)

    a_v = a if a > 0 else ''
    d_v = d if d > 0 else ''

    d_a = '<div class="added progress-bar" style="width:%s%%">%s</div>' % (a_p,
                                                                           a_v)
    d_d = '<div class="deleted progress-bar" style="width:%s%%">%s</div>' % (
        d_p, d_v)
    return literal(
        '<div class="pull-right progress" style="width:%spx">%s%s</div>' %
        (width, d_a, d_d))
Example #53
0
    def post(self, title, category, question, answer_type, interested_response,
             **kw):
        user = request.identity['user']

        qa = model.Qa.query.get(_id=ObjectId(question))

        #  CASO BASE in cui risco a creare un filtro semplice per definizione e' quella di che venga solamente selezionata una risposta
        if len(interested_response) == 1:
            #  La risposta e' solo una creo un filtro semplice
            created_precondition = model.Precondition(
                _owner=user._id,
                _category=ObjectId(category),
                title=title,
                type='simple',
                condition=[ObjectId(question), interested_response[0]])
        else:
            #  CASO AVANZATO sono state selezionate piu' risposte, devo prima creare tutte i filtri semplici e poi creare quella complessa
            if answer_type == "have_response":
                #  Create one precondition simple for all possibility answer to question
                #  After that create a complex precondition with previous simple precondition
                interested_response = qa.answers

            if answer_type == "what_response":
                #  Create one precondition simple for all selected answer to question
                #  After that create a complex precondition with previous simple precondition

                if len(interested_response) <= 1:
                    response.status_code = 412
                    return dict(
                        errors={
                            'interested_response':
                            _('Please select at least one answer')
                        })

            base_precond = []
            for resp in interested_response:
                prec = model.Precondition(_owner=user._id,
                                          _category=ObjectId(category),
                                          title="%s_%s" %
                                          (qa.title.upper(), resp.upper()),
                                          type='simple',
                                          condition=[ObjectId(question), resp],
                                          public=True,
                                          visible=False)
                base_precond.append(prec)

            condition = []
            for prc in base_precond[:-1]:
                condition.append(prc._id)
                condition.append('or')

            condition.append(base_precond[-1]._id)

            created_precondition = model.Precondition(
                _owner=user._id,
                _category=ObjectId(category),
                title=title,
                type='advanced',
                condition=condition)

        #flash(_("Now you can create an output <a href='%s'>HERE</a>" % lurl('/output?workspace='+ str(category))))

        return dict(precondition_id=str(created_precondition._id), errors=None)
Example #54
0
    def __init__(self, org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers):
        from kallithea.controllers.compare import CompareController
        reviewers = set(reviewers)
        _assert_valid_reviewers(reviewers)

        (org_ref_type,
         org_ref_name,
         org_rev) = org_ref.split(':')
        org_display = h.short_ref(org_ref_type, org_ref_name)
        if org_ref_type == 'rev':
            cs = org_repo.scm_instance.get_changeset(org_rev)
            org_ref = 'branch:%s:%s' % (cs.branch, cs.raw_id)

        (other_ref_type,
         other_ref_name,
         other_rev) = other_ref.split(':')
        if other_ref_type == 'rev':
            cs = other_repo.scm_instance.get_changeset(other_rev)
            other_ref_name = cs.raw_id[:12]
            other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, cs.raw_id)
        other_display = h.short_ref(other_ref_type, other_ref_name)

        cs_ranges, _cs_ranges_not, ancestor_revs = \
            CompareController._get_changesets(org_repo.scm_instance.alias,
                                              other_repo.scm_instance, other_rev, # org and other "swapped"
                                              org_repo.scm_instance, org_rev,
                                              )
        if not cs_ranges:
            raise self.Empty(_('Cannot create empty pull request'))

        if not ancestor_revs:
            ancestor_rev = org_repo.scm_instance.EMPTY_CHANGESET
        elif len(ancestor_revs) == 1:
            ancestor_rev = ancestor_revs[0]
        else:
            raise self.AmbiguousAncestor(
                _('Cannot create pull request - criss cross merge detected, please merge a later %s revision to %s')
                % (other_ref_name, org_ref_name))

        self.revisions = [cs_.raw_id for cs_ in cs_ranges]

        # hack: ancestor_rev is not an other_rev but we want to show the
        # requested destination and have the exact ancestor
        other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, ancestor_rev)

        if not title:
            if org_repo == other_repo:
                title = '%s to %s' % (org_display, other_display)
            else:
                title = '%s#%s to %s#%s' % (org_repo.repo_name, org_display,
                                            other_repo.repo_name, other_display)
        description = description or _('No description')

        self.org_repo = org_repo
        self.other_repo = other_repo
        self.org_ref = org_ref
        self.org_rev = org_rev
        self.other_ref = other_ref
        self.title = title
        self.description = description
        self.owner = owner
        self.reviewers = reviewers

        if not CreatePullRequestAction.is_user_authorized(self.org_repo, self.other_repo):
            raise self.Unauthorized(_('You are not authorized to create the pull request'))
Example #55
0
 def title(self):
     return _('Controllers')
Example #56
0
    def __init__(self, old_pull_request, new_org_rev, new_other_rev, title, description, owner, reviewers):
        self.old_pull_request = old_pull_request

        org_repo = old_pull_request.org_repo
        org_ref_type, org_ref_name, org_rev = old_pull_request.org_ref.split(':')

        other_repo = old_pull_request.other_repo
        other_ref_type, other_ref_name, other_rev = old_pull_request.other_ref.split(':') # other_rev is ancestor
        #assert other_ref_type == 'branch', other_ref_type # TODO: what if not?

        new_org_ref = '%s:%s:%s' % (org_ref_type, org_ref_name, new_org_rev)
        new_other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, new_other_rev)

        self.create_action = CreatePullRequestAction(org_repo, other_repo, new_org_ref, new_other_ref, None, None, owner, reviewers)

        # Generate complete title/description

        old_revisions = set(old_pull_request.revisions)
        revisions = self.create_action.revisions
        new_revisions = [r for r in revisions if r not in old_revisions]
        lost = old_revisions.difference(revisions)

        infos = ['This is a new iteration of %s "%s".' %
                 (h.canonical_url('pullrequest_show', repo_name=old_pull_request.other_repo.repo_name,
                      pull_request_id=old_pull_request.pull_request_id),
                  old_pull_request.title)]

        if lost:
            infos.append(_('Missing changesets since the previous iteration:'))
            for r in old_pull_request.revisions:
                if r in lost:
                    rev_desc = org_repo.get_changeset(r).message.split('\n')[0]
                    infos.append('  %s %s' % (h.short_id(r), rev_desc))

        if new_revisions:
            infos.append(_('New changesets on %s %s since the previous iteration:') % (org_ref_type, org_ref_name))
            for r in reversed(revisions):
                if r in new_revisions:
                    rev_desc = org_repo.get_changeset(r).message.split('\n')[0]
                    infos.append('  %s %s' % (h.short_id(r), h.shorter(rev_desc, 80)))

            if self.create_action.other_ref == old_pull_request.other_ref:
                infos.append(_("Ancestor didn't change - diff since previous iteration:"))
                infos.append(h.canonical_url('compare_url',
                                 repo_name=org_repo.repo_name, # other_repo is always same as repo_name
                                 org_ref_type='rev', org_ref_name=h.short_id(org_rev), # use old org_rev as base
                                 other_ref_type='rev', other_ref_name=h.short_id(new_org_rev),
                                 )) # note: linear diff, merge or not doesn't matter
            else:
                infos.append(_('This iteration is based on another %s revision and there is no simple diff.') % other_ref_name)
        else:
            infos.append(_('No changes found on %s %s since previous iteration.') % (org_ref_type, org_ref_name))
            # TODO: fail?

        v = 2
        m = re.match(r'(.*)\(v(\d+)\)\s*$', title)
        if m is not None:
            title = m.group(1)
            v = int(m.group(2)) + 1
        self.create_action.title = '%s (v%s)' % (title.strip(), v)

        # using a mail-like separator, insert new iteration info in description with latest first
        descriptions = description.replace('\r\n', '\n').split('\n-- \n', 1)
        description = descriptions[0].strip() + '\n\n-- \n' + '\n'.join(infos)
        if len(descriptions) > 1:
            description += '\n\n' + descriptions[1].strip()
        self.create_action.description = description

        if not CreatePullRequestIterationAction.is_user_authorized(self.old_pull_request):
            raise CreatePullRequestAction.Unauthorized(_('You are not authorized to create the pull request'))
Example #57
0
    def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type,
                other_ref_name):
        org_ref_name = org_ref_name.strip()
        other_ref_name = other_ref_name.strip()

        # If merge is True:
        #   Show what org would get if merged with other:
        #   List changesets that are ancestors of other but not of org.
        #   New changesets in org is thus ignored.
        #   Diff will be from common ancestor, and merges of org to other will thus be ignored.
        # If merge is False:
        #   Make a raw diff from org to other, no matter if related or not.
        #   Changesets in one and not in the other will be ignored
        merge = bool(request.GET.get('merge'))
        # fulldiff disables cut_off_limit
        c.fulldiff = request.GET.get('fulldiff')
        # partial uses compare_cs.html template directly
        partial = request.environ.get('HTTP_X_PARTIAL_XHR')
        # is_ajax_preview puts hidden input field with changeset revisions
        c.is_ajax_preview = partial and request.GET.get('is_ajax_preview')
        # swap url for compare_diff page - never partial and never is_ajax_preview
        c.swap_url = h.url('compare_url',
                           repo_name=c.cs_repo.repo_name,
                           org_ref_type=other_ref_type,
                           org_ref_name=other_ref_name,
                           other_repo=c.a_repo.repo_name,
                           other_ref_type=org_ref_type,
                           other_ref_name=org_ref_name,
                           merge=merge or '')

        # set callbacks for generating markup for icons
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = safe_int(request.GET.get('context'), 3)

        c.a_rev = self._get_ref_rev(c.a_repo,
                                    org_ref_type,
                                    org_ref_name,
                                    returnempty=True)
        c.cs_rev = self._get_ref_rev(c.cs_repo, other_ref_type, other_ref_name)

        c.compare_home = False
        c.a_ref_name = org_ref_name
        c.a_ref_type = org_ref_type
        c.cs_ref_name = other_ref_name
        c.cs_ref_type = other_ref_type

        c.cs_ranges, c.cs_ranges_org, c.ancestors = self._get_changesets(
            c.a_repo.scm_instance.alias, c.a_repo.scm_instance, c.a_rev,
            c.cs_repo.scm_instance, c.cs_rev)
        raw_ids = [x.raw_id for x in c.cs_ranges]
        c.cs_comments = c.cs_repo.get_comments(raw_ids)
        c.statuses = c.cs_repo.statuses(raw_ids)

        revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
        c.jsdata = graph_data(c.cs_repo.scm_instance, revs)

        if partial:
            return render('compare/compare_cs.html')

        org_repo = c.a_repo
        other_repo = c.cs_repo

        if merge:
            rev1 = msg = None
            if not c.cs_ranges:
                msg = _('Cannot show empty diff')
            elif not c.ancestors:
                msg = _('No ancestor found for merge diff')
            elif len(c.ancestors) == 1:
                rev1 = c.ancestors[0]
            else:
                msg = _('Multiple merge ancestors found for merge compare')
            if rev1 is None:
                h.flash(msg, category='error')
                log.error(msg)
                raise HTTPNotFound

            # case we want a simple diff without incoming changesets,
            # previewing what will be merged.
            # Make the diff on the other repo (which is known to have other_rev)
            log.debug('Using ancestor %s as rev1 instead of %s', rev1, c.a_rev)
            org_repo = other_repo
        else:  # comparing tips, not necessarily linearly related
            if org_repo != other_repo:
                # TODO: we could do this by using hg unionrepo
                log.error('cannot compare across repos %s and %s', org_repo,
                          other_repo)
                h.flash(_(
                    'Cannot compare repositories without using common ancestor'
                ),
                        category='error')
                raise HTTPBadRequest
            rev1 = c.a_rev

        diff_limit = self.cut_off_limit if not c.fulldiff else None

        log.debug('running diff between %s and %s in %s', rev1, c.cs_rev,
                  org_repo.scm_instance.path)
        txtdiff = org_repo.scm_instance.get_diff(
            rev1=rev1,
            rev2=c.cs_rev,
            ignore_whitespace=ignore_whitespace,
            context=line_context)

        diff_processor = diffs.DiffProcessor(txtdiff or '',
                                             format='gitdiff',
                                             diff_limit=diff_limit)
        _parsed = diff_processor.prepare()

        c.limited_diff = False
        if isinstance(_parsed, LimitedDiffContainer):
            c.limited_diff = True

        c.file_diff_data = []
        c.lines_added = 0
        c.lines_deleted = 0
        for f in _parsed:
            st = f['stats']
            c.lines_added += st['added']
            c.lines_deleted += st['deleted']
            filename = f['filename']
            fid = h.FID('', filename)
            diff = diff_processor.as_html(enable_comments=False,
                                          parsed_lines=[f])
            c.file_diff_data.append((fid, None, f['operation'],
                                     f['old_filename'], filename, diff, st))

        return render('compare/compare_diff.html')
Example #58
0
    def make_description(self, notification, show_age=True):
        """
        Creates a human readable description based on properties
        of notification object
        """
        #alias
        _n = notification

        if show_age:
            return {
                _n.TYPE_CHANGESET_COMMENT:
                _('%(user)s commented on changeset %(age)s'),
                _n.TYPE_MESSAGE:
                _('%(user)s sent message %(age)s'),
                _n.TYPE_MENTION:
                _('%(user)s mentioned you %(age)s'),
                _n.TYPE_REGISTRATION:
                _('%(user)s registered in Kallithea %(age)s'),
                _n.TYPE_PULL_REQUEST:
                _('%(user)s opened new pull request %(age)s'),
                _n.TYPE_PULL_REQUEST_COMMENT:
                _('%(user)s commented on pull request %(age)s'),
            }[notification.type_] % dict(
                user=notification.created_by_user.username,
                age=h.age(notification.created_on),
            )
        else:
            return {
                _n.TYPE_CHANGESET_COMMENT:
                _('%(user)s commented on changeset at %(when)s'),
                _n.TYPE_MESSAGE:
                _('%(user)s sent message at %(when)s'),
                _n.TYPE_MENTION:
                _('%(user)s mentioned you at %(when)s'),
                _n.TYPE_REGISTRATION:
                _('%(user)s registered in Kallithea at %(when)s'),
                _n.TYPE_PULL_REQUEST:
                _('%(user)s opened new pull request at %(when)s'),
                _n.TYPE_PULL_REQUEST_COMMENT:
                _('%(user)s commented on pull request at %(when)s'),
            }[notification.type_] % dict(
                user=notification.created_by_user.username,
                when=h.fmt_date(notification.created_on),
            )
Example #59
0
def create_cs_pr_comment(repo_name,
                         revision=None,
                         pull_request=None,
                         allowed_to_change_status=True):
    """
    Add a comment to the specified changeset or pull request, using POST values
    from the request.

    Comments can be inline (when a file path and line number is specified in
    POST) or general comments.
    A comment can be accompanied by a review status change (accepted, rejected,
    etc.). Pull requests can be closed or deleted.

    Parameter 'allowed_to_change_status' is used for both status changes and
    closing of pull requests. For deleting of pull requests, more specific
    checks are done.
    """

    assert request.environ.get('HTTP_X_PARTIAL_XHR')
    if pull_request:
        pull_request_id = pull_request.pull_request_id
    else:
        pull_request_id = None

    status = request.POST.get('changeset_status')
    close_pr = request.POST.get('save_close')
    delete = request.POST.get('save_delete')
    f_path = request.POST.get('f_path')
    line_no = request.POST.get('line')

    if (status or close_pr or delete) and (f_path or line_no):
        # status votes and closing is only possible in general comments
        raise HTTPBadRequest()

    if not allowed_to_change_status:
        if status or close_pr:
            h.flash(_('No permission to change status'), 'error')
            raise HTTPForbidden()

    if pull_request and delete == "delete":
        if (pull_request.owner_id == request.authuser.user_id
                or h.HasPermissionAny('hg.admin')()
                or h.HasRepoPermissionLevel('admin')(
                    pull_request.org_repo.repo_name)
                or h.HasRepoPermissionLevel('admin')(
                    pull_request.other_repo.repo_name)
            ) and not pull_request.is_closed():
            PullRequestModel().delete(pull_request)
            Session().commit()
            h.flash(_('Successfully deleted pull request %s') %
                    pull_request_id,
                    category='success')
            return {
                'location': h.url('my_pullrequests'),  # or repo pr list?
            }
        raise HTTPForbidden()

    text = request.POST.get('text', '').strip()

    comment = ChangesetCommentsModel().create(
        text=text,
        repo=c.db_repo.repo_id,
        author=request.authuser.user_id,
        revision=revision,
        pull_request=pull_request_id,
        f_path=f_path or None,
        line_no=line_no or None,
        status_change=ChangesetStatus.get_status_lbl(status)
        if status else None,
        closing_pr=close_pr,
    )

    if status:
        ChangesetStatusModel().set_status(
            c.db_repo.repo_id,
            status,
            request.authuser.user_id,
            comment,
            revision=revision,
            pull_request=pull_request_id,
        )

    if pull_request:
        action = 'user_commented_pull_request:%s' % pull_request_id
    else:
        action = 'user_commented_revision:%s' % revision
    action_logger(request.authuser, action, c.db_repo, request.ip_addr)

    if pull_request and close_pr:
        PullRequestModel().close_pull_request(pull_request_id)
        action_logger(request.authuser,
                      'user_closed_pull_request:%s' % pull_request_id,
                      c.db_repo, request.ip_addr)

    Session().commit()

    data = {
        'target_id': h.safeid(request.POST.get('f_path')),
    }
    if comment is not None:
        c.comment = comment
        data.update(comment.get_dict())
        data.update({
            'rendered_text':
            render('changeset/changeset_comment_block.html')
        })

    return data
Example #60
0
 def post_logout(self, came_from=lurl('/')):
     flash(_('We hope to see you soon!'))
     return HTTPFound(location=came_from)