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()
def __before__(self): """ __before__ is called before controller methods and after __call__ """ c.kallithea_version = __version__ rc_config = Setting.get_app_settings() # Visual options c.visual = AttributeDict({}) ## DB stored c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon')) c.visual.show_private_icon = str2bool(rc_config.get('show_private_icon')) c.visual.stylify_metatags = str2bool(rc_config.get('stylify_metatags')) c.visual.dashboard_items = safe_int(rc_config.get('dashboard_items', 100)) c.visual.admin_grid_items = safe_int(rc_config.get('admin_grid_items', 100)) c.visual.repository_fields = str2bool(rc_config.get('repository_fields')) c.visual.show_version = str2bool(rc_config.get('show_version')) c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar')) c.visual.gravatar_url = rc_config.get('gravatar_url') c.ga_code = rc_config.get('ga_code') # TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code if c.ga_code and '<' not in c.ga_code: c.ga_code = '''<script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', '%s']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script>''' % c.ga_code c.site_name = rc_config.get('title') c.clone_uri_tmpl = rc_config.get('clone_uri_tmpl') ## INI stored c.visual.allow_repo_location_change = str2bool(config.get('allow_repo_location_change', True)) c.visual.allow_custom_hooks_settings = str2bool(config.get('allow_custom_hooks_settings', True)) c.instance_id = config.get('instance_id') c.issues_url = config.get('bugtracker', url('issues_url')) # END CONFIG VARS c.repo_name = get_repo_slug(request) # can be empty c.backends = BACKENDS.keys() c.unread_notifications = NotificationModel() \ .get_unread_cnt_for_user(c.authuser.user_id) self.cut_off_limit = safe_int(config.get('cut_off_limit')) c.my_pr_count = PullRequestModel().get_pullrequest_cnt_for_user(c.authuser.user_id) self.sa = meta.Session self.scm_model = ScmModel(self.sa)
def __before__(self): super(FeedController, self).__before__() #common values for feeds self.description = _('Changes on %s repository') self.title = self.title = _('%s %s feed') % (c.site_name, '%s') self.language = 'en-us' self.ttl = "5" import kallithea CONF = kallithea.CONFIG self.include_diff = str2bool(CONF.get('rss_include_diff', False)) self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20)) # we need to protect from parsing huge diffs here other way # we can kill the server self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
def my_account_api_keys_add(self): lifetime = safe_int(request.POST.get('lifetime'), -1) description = request.POST.get('description') ApiKeyModel().create(self.authuser.user_id, description, lifetime) Session().commit() h.flash(_("API key successfully created"), category='success') raise HTTPFound(location=url('my_account_api_keys'))
def index(self): """GET /admin/gists: All items in the collection""" # url('gists') not_default_user = not c.authuser.is_default_user c.show_private = request.GET.get('private') and not_default_user c.show_public = request.GET.get('public') and not_default_user gists = Gist().query() \ .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time())) \ .order_by(Gist.created_on.desc()) # MY private if c.show_private and not c.show_public: gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE) \ .filter(Gist.gist_owner == c.authuser.user_id) # MY public elif c.show_public and not c.show_private: gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) \ .filter(Gist.gist_owner == c.authuser.user_id) # MY public+private elif c.show_private and c.show_public: gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC, Gist.gist_type == Gist.GIST_PRIVATE)) \ .filter(Gist.gist_owner == c.authuser.user_id) # default show ALL public gists if not c.show_public and not c.show_private: gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) c.gists = gists p = safe_int(request.GET.get('page', 1), 1) c.gists_pager = Page(c.gists, page=p, items_per_page=10) return render('admin/gists/index.html')
def validate_python(self, value, state): gr = RepoGroup.get(value) gr_name = gr.group_name if gr else None # None means ROOT location # create repositories with write permission on group is set to true create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() group_admin = HasRepoGroupPermissionAny('group.admin')(gr_name, 'can write into group validator') group_write = HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group validator') forbidden = not (group_admin or (group_write and create_on_write)) can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository') gid = (old_data['repo_group'].get('group_id') if (old_data and 'repo_group' in old_data) else None) value_changed = gid != safe_int(value) new = not old_data # do check if we changed the value, there's a case that someone got # revoked write permissions to a repository, he still created, we # don't need to check permission if he didn't change the value of # groups in form box if value_changed or new: #parent group need to be existing if gr and forbidden: msg = M(self, 'permission_denied', state) raise formencode.Invalid(msg, value, state, error_dict=dict(repo_type=msg) ) ## check if we can write to root location ! elif gr is None and not can_create_repos(): msg = M(self, 'permission_denied_root', state) raise formencode.Invalid(msg, value, state, error_dict=dict(repo_type=msg) )
def _load_changelog_summary(): p = safe_int(request.GET.get('page'), 1) size = safe_int(request.GET.get('size'), 10) def url_generator(**kw): return url('changelog_summary_home', repo_name=c.db_repo.repo_name, size=size, **kw) collection = c.db_repo_scm_instance c.repo_changesets = RepoPage(collection, page=p, items_per_page=size, url=url_generator) page_revisions = [x.raw_id for x in list(c.repo_changesets)] c.comments = c.db_repo.get_comments(page_revisions) c.statuses = c.db_repo.statuses(page_revisions)
def index(self): not_default_user = not request.authuser.is_default_user c.show_private = request.GET.get('private') and not_default_user c.show_public = request.GET.get('public') and not_default_user gists = Gist().query() \ .filter_by(is_expired=False) \ .order_by(Gist.created_on.desc()) # MY private if c.show_private and not c.show_public: gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE) \ .filter(Gist.owner_id == request.authuser.user_id) # MY public elif c.show_public and not c.show_private: gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) \ .filter(Gist.owner_id == request.authuser.user_id) # MY public+private elif c.show_private and c.show_public: gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC, Gist.gist_type == Gist.GIST_PRIVATE)) \ .filter(Gist.owner_id == request.authuser.user_id) # default show ALL public gists if not c.show_public and not c.show_private: gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) c.gists = gists p = safe_int(request.GET.get('page'), 1) c.gists_pager = Page(c.gists, page=p, items_per_page=10) return render('admin/gists/index.html')
def index(self): # Return a rendered template p = safe_int(request.GET.get('page'), 1) c.user = User.get(request.authuser.user_id) c.following = UserFollowing.query() \ .filter(UserFollowing.user_id == request.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() journal = self._get_journal_data(c.following) def url_generator(**kw): return url.current(filter=c.search_term, **kw) c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator) c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('journal/journal_data.html') repos_list = Repository.query(sorted=True) \ .filter_by(owner_id=request.authuser.user_id).all() repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, admin=True) #data used to render the grid c.data = repos_data return render('journal/journal.html')
def __init__(self, reponame, username, *args, **kwargs): from kallithea import CONFIG from kallithea.lib.utils2 import safe_int _code = CONFIG.get('lock_ret_code') self.code = safe_int(_code, self.code) self.title = self.explanation = ('Repository `%s` locked by ' 'user `%s`' % (reponame, username)) super(HTTPLockedRC, self).__init__(*args, **kwargs)
def add_api_key(self, id): c.user = self._get_user_or_raise_if_default(id) lifetime = safe_int(request.POST.get('lifetime'), -1) description = request.POST.get('description') ApiKeyModel().create(c.user.user_id, description, lifetime) Session().commit() h.flash(_("API key successfully created"), category='success') raise HTTPFound(location=url('edit_user_api_keys', id=c.user.user_id))
def show_all(self, repo_name): c.from_ = request.GET.get('from_') or '' c.closed = request.GET.get('closed') or '' c.pull_requests = PullRequestModel().get_all(repo_name, from_=c.from_, closed=c.closed) c.repo_name = repo_name p = safe_int(request.GET.get('page', 1), 1) c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100) return render('/pullrequests/pullrequest_show_all.html')
def index(self, repo_name, revision=None, f_path=None): limit = 2000 default = 100 if request.GET.get('size'): c.size = max(min(safe_int(request.GET.get('size')), limit), 1) session['changelog_size'] = c.size session.save() else: c.size = int(session.get('changelog_size', default)) # min size must be 1 c.size = max(c.size, 1) p = safe_int(request.GET.get('page', 1), 1) branch_name = request.GET.get('branch', None) if (branch_name and branch_name not in c.db_repo_scm_instance.branches and branch_name not in c.db_repo_scm_instance.closed_branches and not revision): return redirect(url('changelog_file_home', repo_name=c.repo_name, revision=branch_name, f_path=f_path or '')) if revision == 'tip': revision = None c.changelog_for_path = f_path try: if f_path: log.debug('generating changelog for path %s' % f_path) # get the history for the file ! tip_cs = c.db_repo_scm_instance.get_changeset() try: collection = tip_cs.get_file_history(f_path) except (NodeDoesNotExistError, ChangesetError): #this node is not present at tip ! try: cs = self.__get_cs(revision, repo_name) collection = cs.get_file_history(f_path) except RepositoryError, e: h.flash(safe_str(e), category='warning') redirect(h.url('changelog_home', repo_name=repo_name)) collection = list(reversed(collection)) else:
def followers(self, repo_name): p = safe_int(request.GET.get('page', 1), 1) repo_id = c.db_repo.repo_id d = UserFollowing.get_repo_followers(repo_id)\ .order_by(UserFollowing.follows_from) c.followers_pager = Page(d, page=p, items_per_page=20) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('/followers/followers_data.html') return render('/followers/followers.html')
def update(self, gist, description, owner, gist_mapping, gist_type, lifetime): gist = self._get_gist(gist) gist_repo = gist.scm_instance lifetime = safe_int(lifetime, -1) if lifetime == 0: # preserve old value gist_expires = gist.gist_expires else: gist_expires = time.time() + (lifetime * 60) if lifetime != -1 else -1 #calculate operation type based on given data gist_mapping_op = {} for k, v in gist_mapping.items(): # add, mod, del if not v['org_filename'] and v['filename']: op = 'add' elif v['org_filename'] and not v['filename']: op = 'del' else: op = 'mod' v['op'] = op gist_mapping_op[k] = v gist.gist_description = description gist.gist_expires = gist_expires gist.owner = owner gist.gist_type = gist_type self.sa.add(gist) self.sa.flush() message = 'updated file' message += 's: ' if len(gist_mapping) > 1 else ': ' message += ', '.join([x for x in gist_mapping]) #fake Kallithea Repository object fake_repo = AttributeDict(dict( repo_name=gist_repo.path, scm_instance_no_cache=lambda: gist_repo, )) self._store_metadata(gist_repo, gist.gist_id, gist.gist_access_id, owner.user_id, gist.gist_type, gist.gist_expires) ScmModel().update_nodes( user=owner.user_id, repo=fake_repo, message=message, nodes=gist_mapping_op, trigger_push_hook=False ) return gist
def add_api_key(self, id): c.user = User.get_or_404(id) if c.user.username == User.DEFAULT_USER: h.flash(_("You can't edit this user"), category='warning') return redirect(url('users')) lifetime = safe_int(request.POST.get('lifetime'), -1) description = request.POST.get('description') ApiKeyModel().create(c.user.user_id, description, lifetime) Session().commit() h.flash(_("Api key successfully created"), category='success') return redirect(url('edit_user_api_keys', id=c.user.user_id))
def forks(self, repo_name): p = safe_int(request.GET.get('page'), 1) repo_id = c.db_repo.repo_id d = [] for r in Repository.get_repo_forks(repo_id): if not HasRepoPermissionLevel('read')(r.repo_name, 'get forks check'): continue d.append(r) c.forks_pager = Page(d, page=p, items_per_page=20) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('/forks/forks_data.html') return render('/forks/forks.html')
def show_all(self, repo_name): c.from_ = request.GET.get('from_') or '' c.closed = request.GET.get('closed') or '' p = safe_int(request.GET.get('page'), 1) q = PullRequest.query(include_closed=c.closed, sorted=True) if c.from_: q = q.filter_by(org_repo=c.db_repo) else: q = q.filter_by(other_repo=c.db_repo) c.pull_requests = q.all() c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100) return render('/pullrequests/pullrequest_show_all.html')
def delete_perms(self, group_name): """ DELETE an existing repository group permission user :param group_name: """ 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 c.authuser.is_admin: if obj_type == 'user' and c.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') recursive = request.POST.get('recursive', 'none') if obj_type == 'user': RepoGroupModel().delete_permission(repo_group=group_name, obj=obj_id, obj_type='user', recursive=recursive) elif obj_type == 'user_group': RepoGroupModel().delete_permission(repo_group=group_name, obj=obj_id, obj_type='user_group', recursive=recursive) Session().commit() except Exception: log.error(traceback.format_exc()) h.flash(_('An error occurred during revoking of permission'), category='error') raise HTTPInternalServerError()
def edit_permissions_revoke(self, repo_name): 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 obj_type == 'user': RepoModel().revoke_user_permission(repo=repo_name, user=obj_id) elif obj_type == 'user_group': RepoModel().revoke_user_group_permission( repo=repo_name, group_name=obj_id ) #TODO: implement this #action_logger(self.authuser, 'admin_revoked_repo_permissions', # repo_name, self.ip_addr, self.sa) Session().commit() except Exception: log.error(traceback.format_exc()) h.flash(_('An error occurred during revoking of permission'), category='error') raise HTTPInternalServerError()
def show_id(cs): """ Configurable function that shows ID by default it's r123:fffeeefffeee :param cs: changeset instance """ from kallithea import CONFIG def_len = safe_int(CONFIG.get('show_sha_length', 12)) show_rev = str2bool(CONFIG.get('show_revision_number', False)) raw_id = cs.raw_id[:def_len] if show_rev: return 'r%s:%s' % (cs.revision, raw_id) else: return raw_id
def new(self): if HasPermissionAny('hg.admin')('group create'): #we're global admin, we're ok and we can create TOP level groups pass else: # we pass in parent group into creation form, thus we know # what would be the group, we can check perms here ! group_id = safe_int(request.GET.get('parent_group')) group = RepoGroup.get(group_id) if group_id else None group_name = group.group_name if group else None if HasRepoGroupPermissionLevel('admin')(group_name, 'group create'): pass else: raise HTTPForbidden() self.__load_defaults() return render('admin/repo_groups/repo_group_add.html')
def new(self): """GET /repo_groups/new: Form to create a new item""" # url('new_repos_group') if HasPermissionAll('hg.admin')('group create'): #we're global admin, we're ok and we can create TOP level groups pass else: # we pass in parent group into creation form, thus we know # what would be the group, we can check perms here ! group_id = safe_int(request.GET.get('parent_group')) group = RepoGroup.get(group_id) if group_id else None group_name = group.group_name if group else None if HasRepoGroupPermissionAll('group.admin')(group_name, 'group create'): pass else: return abort(403) self.__load_defaults() return render('admin/repo_groups/repo_group_add.html')
def index(self, format='html'): c.user = request.authuser notif = NotificationModel().query_for_user(request.authuser.user_id, filter_=request.GET.getall('type')) p = safe_int(request.GET.get('page'), 1) c.notifications = Page(notif, page=p, items_per_page=10) c.pull_request_type = Notification.TYPE_PULL_REQUEST c.comment_type = [Notification.TYPE_CHANGESET_COMMENT, Notification.TYPE_PULL_REQUEST_COMMENT] _current_filter = request.GET.getall('type') c.current_filter = 'all' if _current_filter == [c.pull_request_type]: c.current_filter = 'pull_request' elif _current_filter == c.comment_type: c.current_filter = 'comment' return render('admin/notifications/notifications.html')
def public_journal(self): # Return a rendered template p = safe_int(request.GET.get('page'), 1) c.following = UserFollowing.query() \ .filter(UserFollowing.user_id == request.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() journal = self._get_journal_data(c.following) c.journal_pager = Page(journal, page=p, items_per_page=20) c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('journal/journal_data.html') return render('journal/public_journal.html')
def __changes(self, cs): changes = [] rss_cut_off_limit = safe_int(CONFIG.get('rss_cut_off_limit', 32 * 1024)) diff_processor = DiffProcessor(cs.diff(), diff_limit=rss_cut_off_limit) _parsed = diff_processor.prepare(inline_diff=False) limited_diff = False if isinstance(_parsed, LimitedDiffContainer): limited_diff = True for st in _parsed: st.update({'added': st['stats']['added'], 'removed': st['stats']['deleted']}) changes.append('\n %(operation)s %(filename)s ' '(%(added)s lines added, %(removed)s lines removed)' % st) if limited_diff: changes = changes + ['\n ' + _('Changeset was too big and was cut off...')] return diff_processor, changes
def _get_feed_from_cache(key, kind): feed = Rss201rev2Feed( title=_('%s %s feed') % (c.site_name, repo_name), link=h.canonical_url('summary_home', repo_name=repo_name), description=_('Changes on %s repository') % repo_name, language=language, ttl=ttl ) rss_items_per_page = safe_int(CONFIG.get('rss_items_per_page', 20)) for cs in reversed(list(c.db_repo_scm_instance[-rss_items_per_page:])): feed.add_item(title=self._get_title(cs), link=h.canonical_url('changeset_home', repo_name=repo_name, revision=cs.raw_id), author_name=cs.author, description=''.join(self.__get_desc(cs)), pubdate=cs.date, ) response.content_type = feed.mime_type return feed.writeString('utf-8')
def index(self): users_log = UserLog.query()\ .options(joinedload(UserLog.user))\ .options(joinedload(UserLog.repository)) #FILTERING c.search_term = request.GET.get('filter') users_log = _journal_filter(users_log, c.search_term) users_log = users_log.order_by(UserLog.action_date.desc()) p = safe_int(request.GET.get('page', 1), 1) def url_generator(**kw): return url.current(filter=c.search_term, **kw) c.users_log = Page(users_log, page=p, items_per_page=10, url=url_generator) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('admin/admin_log.html') return render('admin/admin.html')
def __changes(self, cs): changes = [] rss_cut_off_limit = safe_int(CONFIG.get('rss_cut_off_limit', 32 * 1024)) diff_processor = DiffProcessor(cs.diff(), diff_limit=rss_cut_off_limit) _parsed = diff_processor.prepare(inline_diff=False) limited_diff = False if isinstance(_parsed, LimitedDiffContainer): limited_diff = True for st in _parsed: st.update({ 'added': st['stats']['added'], 'removed': st['stats']['deleted'] }) changes.append( '\n %(operation)s %(filename)s ' '(%(added)s lines added, %(removed)s lines removed)' % st) if limited_diff: changes = changes + [ '\n ' + _('Changeset was too big and was cut off...') ] return diff_processor, changes
def index(self, format='html'): """GET /_admin/notifications: All items in the collection""" # url('notifications') c.user = self.authuser notif = NotificationModel().get_for_user( self.authuser.user_id, filter_=request.GET.getall('type')) p = safe_int(request.GET.get('page', 1), 1) c.notifications = Page(notif, page=p, items_per_page=10) c.pull_request_type = Notification.TYPE_PULL_REQUEST c.comment_type = [ Notification.TYPE_CHANGESET_COMMENT, Notification.TYPE_PULL_REQUEST_COMMENT ] _current_filter = request.GET.getall('type') c.current_filter = 'all' if _current_filter == [c.pull_request_type]: c.current_filter = 'pull_request' elif _current_filter == c.comment_type: c.current_filter = 'comment' return render('admin/notifications/notifications.html')
def _get_feed_from_cache(key, kind): feed = Rss201rev2Feed( title=_('%s %s feed') % (c.site_name, repo_name), link=h.canonical_url('summary_home', repo_name=repo_name), description=_('Changes on %s repository') % repo_name, language=language, ttl=ttl) rss_items_per_page = safe_int(CONFIG.get('rss_items_per_page', 20)) for cs in reversed( list(c.db_repo_scm_instance[-rss_items_per_page:])): feed.add_item( title=self._get_title(cs), link=h.canonical_url('changeset_home', repo_name=repo_name, revision=cs.raw_id), author_name=cs.author, description=''.join(self.__get_desc(cs)), pubdate=cs.date, ) response.content_type = feed.mime_type return feed.writeString('utf-8')
def show_all(self, repo_name): c.from_ = request.GET.get('from_') or '' c.closed = request.GET.get('closed') or '' url_params = {} if c.from_: url_params['from_'] = 1 if c.closed: url_params['closed'] = 1 p = safe_int(request.GET.get('page'), 1) q = PullRequest.query(include_closed=c.closed, sorted=True) if c.from_: q = q.filter_by(org_repo=c.db_repo) else: q = q.filter_by(other_repo=c.db_repo) c.pull_requests = q.all() c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100, **url_params) return render('/pullrequests/pullrequest_show_all.html')
def index(self): not_default_user = not request.authuser.is_default_user c.show_private = request.GET.get('private') and not_default_user c.show_public = request.GET.get('public') and not_default_user url_params = {} if c.show_public: url_params['public'] = 1 elif c.show_private: url_params['private'] = 1 gists = Gist().query() \ .filter_by(is_expired=False) \ .order_by(Gist.created_on.desc()) # MY private if c.show_private and not c.show_public: gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE) \ .filter(Gist.owner_id == request.authuser.user_id) # MY public elif c.show_public and not c.show_private: gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) \ .filter(Gist.owner_id == request.authuser.user_id) # MY public+private elif c.show_private and c.show_public: gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC, Gist.gist_type == Gist.GIST_PRIVATE)) \ .filter(Gist.owner_id == request.authuser.user_id) # default show ALL public gists if not c.show_public and not c.show_private: gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) c.gists = gists p = safe_int(request.GET.get('page'), 1) c.gists_pager = Page(c.gists, page=p, items_per_page=10, **url_params) return render('admin/gists/index.html')
def validate_python(self, value, state): gr = RepoGroup.get(value) gr_name = gr.group_name if gr else None # None means ROOT location # create repositories with write permission on group is set to true create_on_write = HasPermissionAny( 'hg.create.write_on_repogroup.true')() group_admin = HasRepoGroupPermissionAny('group.admin')( gr_name, 'can write into group validator') group_write = HasRepoGroupPermissionAny('group.write')( gr_name, 'can write into group validator') forbidden = not (group_admin or (group_write and create_on_write)) can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository') gid = (old_data['repo_group'].get('group_id') if (old_data and 'repo_group' in old_data) else None) value_changed = gid != safe_int(value) new = not old_data # do check if we changed the value, there's a case that someone got # revoked write permissions to a repository, he still created, we # don't need to check permission if he didn't change the value of # groups in form box if value_changed or new: #parent group need to be existing if gr and forbidden: msg = M(self, 'permission_denied', state) raise formencode.Invalid(msg, value, state, error_dict=dict(repo_type=msg)) ## check if we can write to root location ! elif gr is None and not can_create_repos(): msg = M(self, 'permission_denied_root', state) raise formencode.Invalid(msg, value, state, error_dict=dict(repo_type=msg))
def create(self, description, owner, gist_mapping, gist_type=Gist.GIST_PUBLIC, lifetime=-1): """ :param description: description of the gist :param owner: user who created this gist :param gist_mapping: mapping {filename:{'content':content},...} :param gist_type: type of gist private/public :param lifetime: in minutes, -1 == forever """ owner = self._get_user(owner) gist_id = safe_unicode(unique_id(20)) lifetime = safe_int(lifetime, -1) gist_expires = time.time() + (lifetime * 60) if lifetime != -1 else -1 log.debug('set GIST expiration date to: %s', time_to_datetime(gist_expires) if gist_expires != -1 else 'forever') #create the Database version gist = Gist() gist.gist_description = description gist.gist_access_id = gist_id gist.gist_owner = owner.user_id gist.gist_expires = gist_expires gist.gist_type = safe_unicode(gist_type) self.sa.add(gist) self.sa.flush() if gist_type == Gist.GIST_PUBLIC: # use DB ID for easy to use GIST ID gist_id = safe_unicode(gist.gist_id) gist.gist_access_id = gist_id self.sa.add(gist) gist_repo_path = os.path.join(GIST_STORE_LOC, gist_id) log.debug('Creating new %s GIST repo in %s', gist_type, gist_repo_path) repo = RepoModel()._create_filesystem_repo( repo_name=gist_id, repo_type='hg', repo_group=GIST_STORE_LOC) processed_mapping = {} for filename in gist_mapping: if filename != os.path.basename(filename): raise Exception('Filename cannot be inside a directory') content = gist_mapping[filename]['content'] #TODO: expand support for setting explicit lexers # if lexer is None: # try: # guess_lexer = pygments.lexers.guess_lexer_for_filename # lexer = guess_lexer(filename,content) # except pygments.util.ClassNotFound: # lexer = 'text' processed_mapping[filename] = {'content': content} # now create single multifile commit message = 'added file' message += 's: ' if len(processed_mapping) > 1 else ': ' message += ', '.join([x for x in processed_mapping]) #fake Kallithea Repository object fake_repo = AttributeDict(dict( repo_name=gist_repo_path, scm_instance_no_cache=lambda: repo, )) ScmModel().create_nodes( user=owner.user_id, repo=fake_repo, message=message, nodes=processed_mapping, trigger_push_hook=False ) self._store_metadata(repo, gist.gist_id, gist.gist_access_id, owner.user_id, gist.gist_type, gist.gist_expires) return gist
def show(self, repo_name, pull_request_id, extra=None): repo_model = RepoModel() c.users_array = repo_model.get_users_js() c.user_groups_array = repo_model.get_user_groups_js() c.pull_request = PullRequest.get_or_404(pull_request_id) c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request) cc_model = ChangesetCommentsModel() cs_model = ChangesetStatusModel() # pull_requests repo_name we opened it against # ie. other_repo must match if repo_name != c.pull_request.other_repo.repo_name: raise HTTPNotFound # load compare data into template context c.cs_repo = c.pull_request.org_repo (c.cs_ref_type, c.cs_ref_name, c.cs_rev) = c.pull_request.org_ref.split(':') c.a_repo = c.pull_request.other_repo (c.a_ref_type, c.a_ref_name, c.a_rev) = c.pull_request.other_ref.split(':') # a_rev is ancestor org_scm_instance = c.cs_repo.scm_instance # property with expensive cache invalidation check!!! try: c.cs_ranges = [org_scm_instance.get_changeset(x) for x in c.pull_request.revisions] except ChangesetDoesNotExistError: c.cs_ranges = [] h.flash(_('Revision %s not found in %s') % (x, c.cs_repo.repo_name), 'error') c.cs_ranges_org = None # not stored and not important and moving target - could be calculated ... revs = [ctx.revision for ctx in reversed(c.cs_ranges)] c.jsdata = graph_data(org_scm_instance, revs) c.is_range = False try: if c.a_ref_type == 'rev': # this looks like a free range where target is ancestor cs_a = org_scm_instance.get_changeset(c.a_rev) root_parents = c.cs_ranges[0].parents c.is_range = cs_a in root_parents #c.merge_root = len(root_parents) > 1 # a range starting with a merge might deserve a warning except ChangesetDoesNotExistError: # probably because c.a_rev not found pass except IndexError: # probably because c.cs_ranges is empty, probably because revisions are missing pass avail_revs = set() avail_show = [] c.cs_branch_name = c.cs_ref_name c.a_branch_name = None other_scm_instance = c.a_repo.scm_instance c.update_msg = "" c.update_msg_other = "" try: if not c.cs_ranges: c.update_msg = _('Error: changesets not found when displaying pull request from %s.') % c.cs_rev elif org_scm_instance.alias == 'hg' and c.a_ref_name != 'ancestor': if c.cs_ref_type != 'branch': c.cs_branch_name = org_scm_instance.get_changeset(c.cs_ref_name).branch # use ref_type ? c.a_branch_name = c.a_ref_name if c.a_ref_type != 'branch': try: c.a_branch_name = other_scm_instance.get_changeset(c.a_ref_name).branch # use ref_type ? except EmptyRepositoryError: c.a_branch_name = 'null' # not a branch name ... but close enough # candidates: descendants of old head that are on the right branch # and not are the old head itself ... # and nothing at all if old head is a descendant of target ref name if not c.is_range and other_scm_instance._repo.revs('present(%s)::&%s', c.cs_ranges[-1].raw_id, c.a_branch_name): c.update_msg = _('This pull request has already been merged to %s.') % c.a_branch_name elif c.pull_request.is_closed(): c.update_msg = _('This pull request has been closed and can not be updated.') else: # look for descendants of PR head on source branch in org repo avail_revs = org_scm_instance._repo.revs('%s:: & branch(%s)', revs[0], c.cs_branch_name) if len(avail_revs) > 1: # more than just revs[0] # also show changesets that not are descendants but would be merged in targethead = other_scm_instance.get_changeset(c.a_branch_name).raw_id if org_scm_instance.path != other_scm_instance.path: # Note: org_scm_instance.path must come first so all # valid revision numbers are 100% org_scm compatible # - both for avail_revs and for revset results hgrepo = unionrepo.unionrepository(org_scm_instance.baseui, org_scm_instance.path, other_scm_instance.path) else: hgrepo = org_scm_instance._repo show = set(hgrepo.revs('::%ld & !::parents(%s) & !::%s', avail_revs, revs[0], targethead)) c.update_msg = _('The following additional changes are available on %s:') % c.cs_branch_name else: show = set() avail_revs = set() # drop revs[0] c.update_msg = _('No additional changesets found for iterating on this pull request.') # TODO: handle branch heads that not are tip-most brevs = org_scm_instance._repo.revs('%s - %ld - %s', c.cs_branch_name, avail_revs, revs[0]) if brevs: # also show changesets that are on branch but neither ancestors nor descendants show.update(org_scm_instance._repo.revs('::%ld - ::%ld - ::%s', brevs, avail_revs, c.a_branch_name)) show.add(revs[0]) # make sure graph shows this so we can see how they relate c.update_msg_other = _('Note: Branch %s has another head: %s.') % (c.cs_branch_name, h.short_id(org_scm_instance.get_changeset((max(brevs))).raw_id)) avail_show = sorted(show, reverse=True) elif org_scm_instance.alias == 'git': c.cs_repo.scm_instance.get_changeset(c.cs_rev) # check it exists - raise ChangesetDoesNotExistError if not c.update_msg = _("Git pull requests don't support iterating yet.") except ChangesetDoesNotExistError: c.update_msg = _('Error: some changesets not found when displaying pull request from %s.') % c.cs_rev c.avail_revs = avail_revs c.avail_cs = [org_scm_instance.get_changeset(r) for r in avail_show] c.avail_jsdata = graph_data(org_scm_instance, avail_show) 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) ignore_whitespace = request.GET.get('ignorews') == '1' line_context = safe_int(request.GET.get('context'), 3) c.ignorews_url = _ignorews_url c.context_url = _context_url c.fulldiff = request.GET.get('fulldiff') diff_limit = self.cut_off_limit if not c.fulldiff else None # we swap org/other ref since we run a simple diff on one repo log.debug('running diff between %s and %s in %s', c.a_rev, c.cs_rev, org_scm_instance.path) try: txtdiff = org_scm_instance.get_diff(rev1=safe_str(c.a_rev), rev2=safe_str(c.cs_rev), ignore_whitespace=ignore_whitespace, context=line_context) except ChangesetDoesNotExistError: txtdiff = _("The diff can't be shown - the PR revisions could not be found.") 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=True, parsed_lines=[f]) c.file_diff_data.append((fid, None, f['operation'], f['old_filename'], filename, diff, st)) # inline comments c.inline_cnt = 0 c.inline_comments = cc_model.get_inline_comments( c.db_repo.repo_id, pull_request=pull_request_id) # count inline comments for __, lines in c.inline_comments: for comments in lines.values(): c.inline_cnt += len(comments) # comments c.comments = cc_model.get_comments(c.db_repo.repo_id, pull_request=pull_request_id) # (badly named) pull-request status calculation based on reviewer votes (c.pull_request_reviewers, c.pull_request_pending_reviewers, c.current_voting_result, ) = cs_model.calculate_pull_request_result(c.pull_request) c.changeset_statuses = ChangesetStatus.STATUSES c.is_ajax_preview = False c.ancestors = None # [c.a_rev] ... but that is shown in an other way return render('/pullrequests/pullrequest_show.html')
def _before(self, *args, **kwargs): """ _before is called before controller methods and after __call__ """ if request.needs_csrf_check: # CSRF protection: Whenever a request has ambient authority (whether # through a session cookie or its origin IP address), it must include # the correct token, unless the HTTP method is GET or HEAD (and thus # guaranteed to be side effect free. In practice, the only situation # where we allow side effects without ambient authority is when the # authority comes from an API key; and that is handled above. from kallithea.lib import helpers as h token = request.POST.get(h.session_csrf_secret_name) if not token or token != h.session_csrf_secret_token(): log.error('CSRF check failed') raise webob.exc.HTTPForbidden() c.kallithea_version = __version__ rc_config = Setting.get_app_settings() # Visual options c.visual = AttributeDict({}) ## DB stored c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon')) c.visual.show_private_icon = str2bool( rc_config.get('show_private_icon')) c.visual.stylify_metalabels = str2bool( rc_config.get('stylify_metalabels')) c.visual.page_size = safe_int(rc_config.get('dashboard_items', 100)) c.visual.admin_grid_items = safe_int( rc_config.get('admin_grid_items', 100)) c.visual.repository_fields = str2bool( rc_config.get('repository_fields')) c.visual.show_version = str2bool(rc_config.get('show_version')) c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar')) c.visual.gravatar_url = rc_config.get('gravatar_url') c.ga_code = rc_config.get('ga_code') # TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code if c.ga_code and '<' not in c.ga_code: c.ga_code = '''<script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', '%s']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script>''' % c.ga_code c.site_name = rc_config.get('title') c.clone_uri_tmpl = rc_config.get( 'clone_uri_tmpl') or Repository.DEFAULT_CLONE_URI c.clone_ssh_tmpl = rc_config.get( 'clone_ssh_tmpl') or Repository.DEFAULT_CLONE_SSH ## INI stored c.visual.allow_repo_location_change = str2bool( config.get('allow_repo_location_change', True)) c.visual.allow_custom_hooks_settings = str2bool( config.get('allow_custom_hooks_settings', True)) c.ssh_enabled = str2bool(config.get('ssh_enabled', False)) c.instance_id = config.get('instance_id') c.issues_url = config.get('bugtracker', url('issues_url')) # END CONFIG VARS c.repo_name = get_repo_slug(request) # can be empty c.backends = list(BACKENDS) self.cut_off_limit = safe_int(config.get('cut_off_limit')) c.my_pr_count = PullRequest.query(reviewer_id=request.authuser.user_id, include_closed=False).count() self.scm_model = ScmModel()
def __before__(self): """ __before__ is called before controller methods and after __call__ """ c.kallithea_version = __version__ rc_config = Setting.get_app_settings() # Visual options c.visual = AttributeDict({}) ## DB stored c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon')) c.visual.show_private_icon = str2bool( rc_config.get('show_private_icon')) c.visual.stylify_metatags = str2bool(rc_config.get('stylify_metatags')) c.visual.dashboard_items = safe_int( rc_config.get('dashboard_items', 100)) c.visual.admin_grid_items = safe_int( rc_config.get('admin_grid_items', 100)) c.visual.repository_fields = str2bool( rc_config.get('repository_fields')) c.visual.show_version = str2bool(rc_config.get('show_version')) c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar')) c.visual.gravatar_url = rc_config.get('gravatar_url') c.ga_code = rc_config.get('ga_code') # TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code if c.ga_code and '<' not in c.ga_code: c.ga_code = '''<script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', '%s']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script>''' % c.ga_code c.site_name = rc_config.get('title') c.clone_uri_tmpl = rc_config.get('clone_uri_tmpl') ## INI stored c.visual.allow_repo_location_change = str2bool( config.get('allow_repo_location_change', True)) c.visual.allow_custom_hooks_settings = str2bool( config.get('allow_custom_hooks_settings', True)) c.instance_id = config.get('instance_id') c.issues_url = config.get('bugtracker', url('issues_url')) # END CONFIG VARS c.repo_name = get_repo_slug(request) # can be empty c.backends = BACKENDS.keys() c.unread_notifications = NotificationModel()\ .get_unread_cnt_for_user(c.authuser.user_id) self.cut_off_limit = safe_int(config.get('cut_off_limit')) c.my_pr_count = PullRequestModel().get_pullrequest_cnt_for_user( c.authuser.user_id) self.sa = meta.Session self.scm_model = ScmModel(self.sa)
def index(self, repo_name=None): c.repo_name = repo_name c.formated_results = [] c.runtime = '' c.cur_query = request.GET.get('q', None) c.cur_type = request.GET.get('type', 'content') c.cur_search = search_type = {'content': 'content', 'commit': 'message', 'path': 'path', 'repository': 'repository' }.get(c.cur_type, 'content') index_name = { 'content': IDX_NAME, 'commit': CHGSET_IDX_NAME, 'path': IDX_NAME }.get(c.cur_type, IDX_NAME) schema_defn = { 'content': SCHEMA, 'commit': CHGSETS_SCHEMA, 'path': SCHEMA }.get(c.cur_type, SCHEMA) log.debug('IDX: %s', index_name) log.debug('SCHEMA: %s', schema_defn) if c.cur_query: cur_query = c.cur_query.lower() log.debug(cur_query) if c.cur_query: p = safe_int(request.GET.get('page'), 1) highlight_items = set() try: idx = open_dir(config['app_conf']['index_dir'], indexname=index_name) searcher = idx.searcher() qp = QueryParser(search_type, schema=schema_defn) if c.repo_name: # use "repository_rawname:" instead of "repository:" # for case-sensitive matching cur_query = u'repository_rawname:%s %s' % (c.repo_name, cur_query) try: query = qp.parse(unicode(cur_query)) # extract words for highlight if isinstance(query, Phrase): highlight_items.update(query.words) elif isinstance(query, Prefix): highlight_items.add(query.text) else: for i in query.all_terms(): if i[0] in ['content', 'message']: highlight_items.add(i[1]) matcher = query.matcher(searcher) log.debug('query: %s', query) log.debug('hl terms: %s', highlight_items) results = searcher.search(query) res_ln = len(results) c.runtime = '%s results (%.3f seconds)' % ( res_ln, results.runtime ) def url_generator(**kw): q = urllib.quote(safe_str(c.cur_query)) return update_params("?q=%s&type=%s" \ % (q, safe_str(c.cur_type)), **kw) repo_location = RepoModel().repos_path c.formated_results = Page( WhooshResultWrapper(search_type, searcher, matcher, highlight_items, repo_location), page=p, item_count=res_ln, items_per_page=10, url=url_generator ) except QueryParserError: c.runtime = _('Invalid search query. Try quoting it.') searcher.close() except (EmptyIndexError, IOError): log.error(traceback.format_exc()) log.error('Empty Index data') c.runtime = _('There is no index to search in. ' 'Please run whoosh indexer') except Exception: log.error(traceback.format_exc()) c.runtime = _('An error occurred during search operation.') # Return a rendered template return render('/search/search.html')
def index(self, repo_name, revision=None, f_path=None): # Fix URL after page size form submission via GET # TODO: Somehow just don't send this extra junk in the GET URL if request.GET.get('set'): request.GET.pop('set', None) if revision is None: raise HTTPFound(location=url( 'changelog_home', repo_name=repo_name, **request.GET)) raise HTTPFound(location=url('changelog_file_home', repo_name=repo_name, revision=revision, f_path=f_path, **request.GET)) limit = 2000 default = 100 if request.GET.get('size'): c.size = max(min(safe_int(request.GET.get('size')), limit), 1) session['changelog_size'] = c.size session.save() else: c.size = int(session.get('changelog_size', default)) # min size must be 1 c.size = max(c.size, 1) p = safe_int(request.GET.get('page'), 1) branch_name = request.GET.get('branch', None) if (branch_name and branch_name not in c.db_repo_scm_instance.branches and branch_name not in c.db_repo_scm_instance.closed_branches and not revision): raise HTTPFound(location=url('changelog_file_home', repo_name=c.repo_name, revision=branch_name, f_path=f_path or '')) if revision == 'tip': revision = None c.changelog_for_path = f_path try: if f_path: log.debug('generating changelog for path %s', f_path) # get the history for the file ! tip_cs = c.db_repo_scm_instance.get_changeset() try: collection = tip_cs.get_file_history(f_path) except (NodeDoesNotExistError, ChangesetError): #this node is not present at tip ! try: cs = self.__get_cs(revision, repo_name) collection = cs.get_file_history(f_path) except RepositoryError as e: h.flash(safe_str(e), category='warning') raise HTTPFound(location=h.url('changelog_home', repo_name=repo_name)) collection = list(reversed(collection)) else: collection = c.db_repo_scm_instance.get_changesets( start=0, end=revision, branch_name=branch_name) c.total_cs = len(collection) c.pagination = RepoPage( collection, page=p, item_count=c.total_cs, items_per_page=c.size, branch=branch_name, ) page_revisions = [x.raw_id for x in c.pagination] c.comments = c.db_repo.get_comments(page_revisions) c.statuses = c.db_repo.statuses(page_revisions) except EmptyRepositoryError as e: h.flash(safe_str(e), category='warning') raise HTTPFound( location=url('summary_home', repo_name=c.repo_name)) except (RepositoryError, ChangesetDoesNotExistError, Exception) as e: log.error(traceback.format_exc()) h.flash(safe_str(e), category='error') raise HTTPFound( location=url('changelog_home', repo_name=c.repo_name)) c.branch_name = branch_name c.branch_filters = [('', _('None'))] + \ [(k, k) for k in c.db_repo_scm_instance.branches.keys()] if c.db_repo_scm_instance.closed_branches: prefix = _('(closed)') + ' ' c.branch_filters += [('-', '-')] + \ [(k, prefix + k) for k in c.db_repo_scm_instance.closed_branches.keys()] revs = [] if not f_path: revs = [x.revision for x in c.pagination] c.jsdata = graph_data(c.db_repo_scm_instance, revs) c.revision = revision # requested revision ref c.first_revision = c.pagination[0] # pagination is never empty here! return render('changelog/changelog.html')
def index(self): # Return a rendered template p = safe_int(request.GET.get('page', 1), 1) c.user = User.get(self.authuser.user_id) c.following = self.sa.query(UserFollowing)\ .filter(UserFollowing.user_id == self.authuser.user_id)\ .options(joinedload(UserFollowing.follows_repository))\ .all() journal = self._get_journal_data(c.following) def url_generator(**kw): return url.current(filter=c.search_term, **kw) c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator) c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('journal/journal_data.html') repos_list = Session().query(Repository)\ .filter(Repository.user_id == self.authuser.user_id)\ .order_by(func.lower(Repository.repo_name)).all() repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, admin=True) #json used to render the grid c.data = json.dumps(repos_data) watched_repos_data = [] ## watched repos _render = RepoModel._render_datatable def quick_menu(repo_name): return _render('quick_menu', repo_name) def repo_lnk(name, rtype, rstate, private, fork_of): return _render('repo_name', name, rtype, rstate, private, fork_of, short_name=False, admin=False) def last_rev(repo_name, cs_cache): return _render('revision', repo_name, cs_cache.get('revision'), cs_cache.get('raw_id'), cs_cache.get('author'), cs_cache.get('message')) def desc(desc): from pylons import tmpl_context as c if c.visual.stylify_metatags: return h.urlify_text(h.desc_stylize(h.truncate(desc, 60))) else: return h.urlify_text(h.truncate(desc, 60)) def repo_actions(repo_name): return _render('repo_actions', repo_name) def owner_actions(user_id, username): return _render('user_name', user_id, username) def toogle_follow(repo_id): return _render('toggle_follow', repo_id) for entry in c.following: repo = entry.follows_repository cs_cache = repo.changeset_cache row = { "menu": quick_menu(repo.repo_name), "raw_name": repo.repo_name.lower(), "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state, repo.private, repo.fork), "last_changeset": last_rev(repo.repo_name, cs_cache), "last_rev_raw": cs_cache.get('revision'), "action": toogle_follow(repo.repo_id) } watched_repos_data.append(row) c.watched_data = json.dumps({ "totalRecords": len(c.following), "startIndex": 0, "sort": "name", "dir": "asc", "records": watched_repos_data }) return render('journal/journal.html')
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 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.cs_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 = None if fulldiff else self.cut_off_limit log.debug('running diff between %s and %s in %s', rev1, c.cs_rev, org_repo.scm_instance.path) raw_diff = diffs.get_diff(org_repo.scm_instance, rev1=rev1, rev2=c.cs_rev, ignore_whitespace=ignore_whitespace, context=line_context) diff_processor = diffs.DiffProcessor(raw_diff, diff_limit=diff_limit) c.limited_diff = diff_processor.limited_diff c.file_diff_data = [] c.lines_added = 0 c.lines_deleted = 0 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('', filename) html_diff = diffs.as_html(enable_comments=False, parsed_lines=[f]) c.file_diff_data.append( (fid, None, f['operation'], f['old_filename'], filename, html_diff, st)) return render('compare/compare_diff.html')
def diff(self, repo_name, f_path): ignore_whitespace = request.GET.get('ignorews') == '1' line_context = safe_int(request.GET.get('context'), 3) diff2 = request.GET.get('diff2', '') diff1 = request.GET.get('diff1', '') or diff2 c.action = request.GET.get('diff') c.no_changes = diff1 == diff2 c.f_path = f_path c.big_diff = False fulldiff = request.GET.get('fulldiff') c.anchor_url = anchor_url c.ignorews_url = _ignorews_url c.context_url = _context_url c.changes = OrderedDict() c.changes[diff2] = [] # special case if we want a show rev only, it's impl here # to reduce JS and callbacks if request.GET.get('show_rev'): if str2bool(request.GET.get('annotate', 'False')): _url = url('files_annotate_home', repo_name=c.repo_name, revision=diff1, f_path=c.f_path) else: _url = url('files_home', repo_name=c.repo_name, revision=diff1, f_path=c.f_path) raise HTTPFound(location=_url) try: if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]: c.changeset_1 = c.db_repo_scm_instance.get_changeset(diff1) try: node1 = c.changeset_1.get_node(f_path) if node1.is_dir(): raise NodeError('%s path is a %s not a file' % (node1, type(node1))) except NodeDoesNotExistError: c.changeset_1 = EmptyChangeset( cs=diff1, revision=c.changeset_1.revision, repo=c.db_repo_scm_instance) node1 = FileNode(f_path, '', changeset=c.changeset_1) else: c.changeset_1 = EmptyChangeset(repo=c.db_repo_scm_instance) node1 = FileNode(f_path, '', changeset=c.changeset_1) if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]: c.changeset_2 = c.db_repo_scm_instance.get_changeset(diff2) try: node2 = c.changeset_2.get_node(f_path) if node2.is_dir(): raise NodeError('%s path is a %s not a file' % (node2, type(node2))) except NodeDoesNotExistError: c.changeset_2 = EmptyChangeset( cs=diff2, revision=c.changeset_2.revision, repo=c.db_repo_scm_instance) node2 = FileNode(f_path, '', changeset=c.changeset_2) else: c.changeset_2 = EmptyChangeset(repo=c.db_repo_scm_instance) node2 = FileNode(f_path, '', changeset=c.changeset_2) except (RepositoryError, NodeError): log.error(traceback.format_exc()) raise HTTPFound(location=url( 'files_home', repo_name=c.repo_name, f_path=f_path)) if c.action == 'download': raw_diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context) diff_name = '%s_vs_%s.diff' % (diff1, diff2) response.content_type = 'text/plain' response.content_disposition = ('attachment; filename=%s' % diff_name) return raw_diff elif c.action == 'raw': raw_diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context) response.content_type = 'text/plain' return raw_diff else: fid = h.FID(diff2, node2.path) line_context_lcl = get_line_ctx(fid, request.GET) ign_whitespace_lcl = get_ignore_ws(fid, request.GET) diff_limit = None if fulldiff else self.cut_off_limit c.a_rev, c.cs_rev, a_path, diff, st, op = diffs.wrapped_diff( filenode_old=node1, filenode_new=node2, diff_limit=diff_limit, ignore_whitespace=ign_whitespace_lcl, line_context=line_context_lcl, enable_comments=False) c.file_diff_data = [(fid, fid, op, a_path, node2.path, diff, st)] return render('files/file_diff.html')
def create(self, description, owner, gist_mapping, gist_type=Gist.GIST_PUBLIC, lifetime=-1): """ :param description: description of the gist :param owner: user who created this gist :param gist_mapping: mapping {filename:{'content':content},...} :param gist_type: type of gist private/public :param lifetime: in minutes, -1 == forever """ owner = self._get_user(owner) gist_id = safe_unicode(unique_id(20)) lifetime = safe_int(lifetime, -1) gist_expires = time.time() + (lifetime * 60) if lifetime != -1 else -1 log.debug('set GIST expiration date to: %s' % (time_to_datetime(gist_expires) if gist_expires != -1 else 'forever')) #create the Database version gist = Gist() gist.gist_description = description gist.gist_access_id = gist_id gist.gist_owner = owner.user_id gist.gist_expires = gist_expires gist.gist_type = safe_unicode(gist_type) self.sa.add(gist) self.sa.flush() if gist_type == Gist.GIST_PUBLIC: # use DB ID for easy to use GIST ID gist_id = safe_unicode(gist.gist_id) gist.gist_access_id = gist_id self.sa.add(gist) gist_repo_path = os.path.join(GIST_STORE_LOC, gist_id) log.debug('Creating new %s GIST repo in %s' % (gist_type, gist_repo_path)) repo = RepoModel()._create_filesystem_repo( repo_name=gist_id, repo_type='hg', repo_group=GIST_STORE_LOC) processed_mapping = {} for filename in gist_mapping: if filename != os.path.basename(filename): raise Exception('Filename cannot be inside a directory') content = gist_mapping[filename]['content'] #TODO: expand support for setting explicit lexers # if lexer is None: # try: # guess_lexer = pygments.lexers.guess_lexer_for_filename # lexer = guess_lexer(filename,content) # except pygments.util.ClassNotFound: # lexer = 'text' processed_mapping[filename] = {'content': content} # now create single multifile commit message = 'added file' message += 's: ' if len(processed_mapping) > 1 else ': ' message += ', '.join([x for x in processed_mapping]) #fake Kallithea Repository object fake_repo = AttributeDict(dict( repo_name=gist_repo_path, scm_instance_no_cache=lambda: repo, )) ScmModel().create_nodes( user=owner.user_id, repo=fake_repo, message=message, nodes=processed_mapping, trigger_push_hook=False ) self._store_metadata(repo, gist.gist_id, gist.gist_access_id, owner.user_id, gist.gist_type, gist.gist_expires) return gist
def index(self, repo_name=None): c.repo_name = repo_name c.formated_results = [] c.runtime = '' c.cur_query = request.GET.get('q', None) c.cur_type = request.GET.get('type', 'content') c.cur_search = search_type = {'content': 'content', 'commit': 'message', 'path': 'path', 'repository': 'repository' }.get(c.cur_type, 'content') index_name = { 'content': IDX_NAME, 'commit': CHGSET_IDX_NAME, 'path': IDX_NAME }.get(c.cur_type, IDX_NAME) schema_defn = { 'content': SCHEMA, 'commit': CHGSETS_SCHEMA, 'path': SCHEMA }.get(c.cur_type, SCHEMA) log.debug('IDX: %s', index_name) log.debug('SCHEMA: %s', schema_defn) if c.cur_query: cur_query = c.cur_query.lower() log.debug(cur_query) if c.cur_query: p = safe_int(request.GET.get('page', 1), 1) highlight_items = set() try: idx = open_dir(config['app_conf']['index_dir'], indexname=index_name) searcher = idx.searcher() qp = QueryParser(search_type, schema=schema_defn) if c.repo_name: cur_query = u'repository:%s %s' % (c.repo_name, cur_query) try: query = qp.parse(unicode(cur_query)) # extract words for highlight if isinstance(query, Phrase): highlight_items.update(query.words) elif isinstance(query, Prefix): highlight_items.add(query.text) else: for i in query.all_terms(): if i[0] in ['content', 'message']: highlight_items.add(i[1]) matcher = query.matcher(searcher) log.debug('query: %s', query) log.debug('hl terms: %s', highlight_items) results = searcher.search(query) res_ln = len(results) c.runtime = '%s results (%.3f seconds)' % ( res_ln, results.runtime ) def url_generator(**kw): q = urllib.quote(safe_str(c.cur_query)) return update_params("?q=%s&type=%s" \ % (q, safe_str(c.cur_type)), **kw) repo_location = RepoModel().repos_path c.formated_results = Page( WhooshResultWrapper(search_type, searcher, matcher, highlight_items, repo_location), page=p, item_count=res_ln, items_per_page=10, url=url_generator ) except QueryParserError: c.runtime = _('Invalid search query. Try quoting it.') searcher.close() except (EmptyIndexError, IOError): log.error(traceback.format_exc()) log.error('Empty Index data') c.runtime = _('There is no index to search in. ' 'Please run whoosh indexer') except Exception: log.error(traceback.format_exc()) c.runtime = _('An error occurred during search operation.') # Return a rendered template return render('/search/search.html')
def index(self, repo_name=None): c.repo_name = repo_name c.formated_results = [] c.runtime = '' c.cur_query = request.GET.get('q', None) c.cur_type = request.GET.get('type', 'content') c.cur_search = search_type = {'content': 'content', 'commit': 'message', 'path': 'path', 'repository': 'repository' }.get(c.cur_type, 'content') index_name = { 'content': IDX_NAME, 'commit': CHGSET_IDX_NAME, 'path': IDX_NAME }.get(c.cur_type, IDX_NAME) schema_defn = { 'content': SCHEMA, 'commit': CHGSETS_SCHEMA, 'path': SCHEMA }.get(c.cur_type, SCHEMA) log.debug('IDX: %s', index_name) log.debug('SCHEMA: %s', schema_defn) if c.cur_query: cur_query = c.cur_query.lower() log.debug(cur_query) if c.cur_query: p = safe_int(request.GET.get('page'), 1) highlight_items = set() index_dir = config['index_dir'] try: if not exists_in(index_dir, index_name): raise EmptyIndexError idx = open_dir(index_dir, indexname=index_name) searcher = idx.searcher() qp = QueryParser(search_type, schema=schema_defn) if c.repo_name: # use "repository_rawname:" instead of "repository:" # for case-sensitive matching cur_query = 'repository_rawname:%s %s' % (c.repo_name, cur_query) try: query = qp.parse(cur_query) # extract words for highlight if isinstance(query, Phrase): highlight_items.update(query.words) elif isinstance(query, Prefix): highlight_items.add(query.text) else: for i in query.all_terms(): if i[0] in ['content', 'message']: highlight_items.add(i[1]) matcher = query.matcher(searcher) log.debug('query: %s', query) log.debug('hl terms: %s', highlight_items) results = searcher.search(query) res_ln = len(results) c.runtime = '%s results (%.3f seconds)' % ( res_ln, results.runtime ) repo_location = RepoModel().repos_path c.formated_results = Page( WhooshResultWrapper(search_type, searcher, matcher, highlight_items, repo_location), page=p, item_count=res_ln, items_per_page=10, type=c.cur_type, q=c.cur_query, ) except QueryParserError: c.runtime = _('Invalid search query. Try quoting it.') searcher.close() except EmptyIndexError: log.error("Empty search index - run 'kallithea-cli index-create' regularly") c.runtime = _('The server has no search index.') except Exception: log.error(traceback.format_exc()) c.runtime = _('An error occurred during search operation.') # Return a rendered template return render('/search/search.html')