##// END OF EJS Templates
ui: introduce user-bookmarks for creation of quick shortcuts
dan -
r3424:c7ed0ba5 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -0,0 +1,30 b''
1 import logging
2
3 from sqlalchemy import *
4
5 from rhodecode.model import meta
6 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
7
8 log = logging.getLogger(__name__)
9
10
11 def upgrade(migrate_engine):
12 """
13 Upgrade operations go here.
14 Don't create your own engine; bind migrate_engine to your metadata
15 """
16 _reset_base(migrate_engine)
17 from rhodecode.lib.dbmigrate.schema import db_4_16_0_1 as db
18
19 db.UserBookmark.__table__.create()
20
21 fixups(db, meta.Session)
22
23
24 def downgrade(migrate_engine):
25 meta = MetaData()
26 meta.bind = migrate_engine
27
28
29 def fixups(models, _SESSION):
30 pass
@@ -0,0 +1,222 b''
1 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2
3 <%def name="form_item(count, position=None, title=None, redirect_url=None, repo=None, repo_group=None)">
4 <tr>
5 <td class="td-align-top" >
6 <div class="label">
7 <label for="position">${_('Position')}:</label>
8 </div>
9 <div class="input">
10 <input type="text" name="position" value="${position or count}" style="width: 40px"/>
11 </div>
12 </td>
13
14 <td>
15 <div class="label">
16 <label for="title">${_('Bookmark title (max 30 characters, optional)')}:</label>
17 </div>
18 <div class="input">
19 <input type="text" name="title" value="${title}" style="width: 300px" maxlength="30"/>
20
21 <div class="field pull-right">
22 <div>
23 <label class="btn-link btn-danger">${_('Clear')}:</label>
24 ${h.checkbox('remove', value=True)}
25 </div>
26 </div>
27 </div>
28
29 <div class="label">
30 <label for="redirect_url">${_('Redirect URL')}:</label>
31 </div>
32 <div class="input">
33 <input type="text" name="redirect_url" value="${redirect_url}" style="width: 600px"/>
34 </div>
35
36
37 <div class="select">
38 % if repo:
39 <div class="label">
40 <label for="redirect_url">${_('Repository template')}:</label>
41 </div>
42 ${dt.repo_name(name=repo.repo_name, rtype=repo.repo_type,rstate=None,private=None,archived=False,fork_of=False)}
43 ${h.hidden('bookmark_repo', repo.repo_id)}
44 % elif repo_group:
45 <div class="label">
46 <label for="redirect_url">${_('Repository group template')}:</label>
47 </div>
48 ${dt.repo_group_name(repo_group.group_name)}
49 ${h.hidden('bookmark_repo_group', repo_group.group_id)}
50 % else:
51 <div class="label">
52 <label for="redirect_url">${_('Template Repository or Repository group')}:</label>
53 </div>
54 ${h.hidden('bookmark_repo', class_='bookmark_repo')}
55 <span style="padding-right:15px">OR</span>
56 ${h.hidden('bookmark_repo_group', class_='bookmark_repo_group')}
57 % endif
58 </div>
59
60 <p class="help-block help-block-inline" >
61 % if repo:
62 ${_('Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog')}
63 % elif repo_group:
64 ${_('Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}')}
65 % else:
66 ${_('Available as full url variables in redirect url. i.e: ${repo_url}, ${repo_group_url}.')}
67 % endif
68 </p>
69 </td>
70
71 </tr>
72 </%def>
73
74 <div class="panel panel-default">
75 <div class="panel-heading">
76 <h3 class="panel-title">${_('Your Bookmarks')}</h3>
77 </div>
78
79 <div class="panel-body">
80 <p>
81 ${_('Store upto 10 bookmark links to favorite repositories, external issue tracker or CI server. ')}
82 <br/>
83 ${_('Bookmarks are accessible from your username dropdown or by keyboard shortcut `g 0-9`')}
84 </p>
85
86 ${h.secure_form(h.route_path('my_account_bookmarks_update'), request=request)}
87 <div class="form-vertical">
88 <table class="rctable">
89 ## generate always 10 entries
90 <input type="hidden" name="__start__" value="bookmarks:sequence"/>
91 % for cnt, item in enumerate((c.bookmark_items + [None for i in range(10)])[:10]):
92 <input type="hidden" name="__start__" value="bookmark:mapping"/>
93 % if item is None:
94 ## empty placehodlder
95 ${form_item(cnt)}
96 % else:
97 ## actual entry
98 ${form_item(cnt, position=item.position, title=item.title, redirect_url=item.redirect_url, repo=item.repository, repo_group=item.repository_group)}
99 % endif
100 <input type="hidden" name="__end__" value="bookmark:mapping"/>
101 % endfor
102 <input type="hidden" name="__end__" value="bookmarks:sequence"/>
103 </table>
104 <div class="buttons">
105 ${h.submit('save',_('Save'),class_="btn")}
106 </div>
107 </div>
108 ${h.end_form()}
109 </div>
110 </div>
111
112 <script>
113 $(document).ready(function(){
114
115
116 var repoFilter = function (data) {
117 var results = [];
118
119 if (!data.results[0]) {
120 return data
121 }
122
123 $.each(data.results[0].children, function () {
124 // replace name to ID for submision
125 this.id = this.repo_id;
126 results.push(this);
127 });
128
129 data.results[0].children = results;
130 return data;
131 };
132
133
134 $(".bookmark_repo").select2({
135 cachedDataSource: {},
136 minimumInputLength: 2,
137 placeholder: "${_('repository')}",
138 dropdownAutoWidth: true,
139 containerCssClass: "drop-menu",
140 dropdownCssClass: "drop-menu-dropdown",
141 formatResult: formatRepoResult,
142 query: $.debounce(250, function (query) {
143 self = this;
144 var cacheKey = query.term;
145 var cachedData = self.cachedDataSource[cacheKey];
146
147 if (cachedData) {
148 query.callback({results: cachedData.results});
149 } else {
150 $.ajax({
151 url: pyroutes.url('repo_list_data'),
152 data: {'query': query.term},
153 dataType: 'json',
154 type: 'GET',
155 success: function (data) {
156 data = repoFilter(data);
157 self.cachedDataSource[cacheKey] = data;
158 query.callback({results: data.results});
159 },
160 error: function (data, textStatus, errorThrown) {
161 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
162 }
163 })
164 }
165 }),
166 });
167
168 var repoGroupFilter = function (data) {
169 var results = [];
170
171 if (!data.results[0]) {
172 return data
173 }
174
175 $.each(data.results[0].children, function () {
176 // replace name to ID for submision
177 this.id = this.repo_group_id;
178 results.push(this);
179 });
180
181 data.results[0].children = results;
182 return data;
183 };
184
185 $(".bookmark_repo_group").select2({
186 cachedDataSource: {},
187 minimumInputLength: 2,
188 placeholder: "${_('repository group')}",
189 dropdownAutoWidth: true,
190 containerCssClass: "drop-menu",
191 dropdownCssClass: "drop-menu-dropdown",
192 formatResult: formatRepoGroupResult,
193 query: $.debounce(250, function (query) {
194 self = this;
195 var cacheKey = query.term;
196 var cachedData = self.cachedDataSource[cacheKey];
197
198 if (cachedData) {
199 query.callback({results: cachedData.results});
200 } else {
201 $.ajax({
202 url: pyroutes.url('repo_group_list_data'),
203 data: {'query': query.term},
204 dataType: 'json',
205 type: 'GET',
206 success: function (data) {
207 data = repoGroupFilter(data);
208 self.cachedDataSource[cacheKey] = data;
209 query.callback({results: data.results});
210 },
211 error: function (data, textStatus, errorThrown) {
212 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
213 }
214 })
215 }
216 })
217 });
218
219
220 });
221
222 </script>
@@ -45,7 +45,7 b' PYRAMID_SETTINGS = {}'
45 45 EXTENSIONS = {}
46 46
47 47 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
48 __dbversion__ = 93 # defines current db version for migrations
48 __dbversion__ = 94 # defines current db version for migrations
49 49 __platform__ = platform.system()
50 50 __license__ = 'AGPLv3, and Commercial License'
51 51 __author__ = 'RhodeCode GmbH'
@@ -56,6 +56,10 b' def includeme(config):'
56 56 pattern='/_repos')
57 57
58 58 config.add_route(
59 name='repo_group_list_data',
60 pattern='/_repo_groups')
61
62 config.add_route(
59 63 name='goto_switcher_data',
60 64 pattern='/_goto_data')
61 65
@@ -172,7 +172,9 b' class HomeView(BaseAppView):'
172 172 'id': obj.group_name,
173 173 'value': org_query,
174 174 'value_display': obj.group_name,
175 'text': obj.group_name,
175 176 'type': 'repo_group',
177 'repo_group_id': obj.group_id,
176 178 'url': h.route_path(
177 179 'repo_group_home', repo_group_name=obj.group_name)
178 180 }
@@ -302,6 +304,33 b' class HomeView(BaseAppView):'
302 304 }
303 305 return data
304 306
307 @LoginRequired()
308 @view_config(
309 route_name='repo_group_list_data', request_method='GET',
310 renderer='json_ext', xhr=True)
311 def repo_group_list_data(self):
312 _ = self.request.translate
313 self.load_default_context()
314
315 query = self.request.GET.get('query')
316
317 log.debug('generating repo group list, query:%s',
318 query)
319
320 res = []
321 repo_groups = self._get_repo_group_list(query)
322 if repo_groups:
323 res.append({
324 'text': _('Repository Groups'),
325 'children': repo_groups
326 })
327
328 data = {
329 'more': False,
330 'results': res
331 }
332 return data
333
305 334 def _get_default_search_queries(self, search_context, searcher, query):
306 335 if not searcher:
307 336 return []
@@ -95,6 +95,18 b' def includeme(config):'
95 95 pattern=ADMIN_PREFIX + '/my_account/watched')
96 96
97 97 config.add_route(
98 name='my_account_bookmarks',
99 pattern=ADMIN_PREFIX + '/my_account/bookmarks')
100
101 config.add_route(
102 name='my_account_bookmarks_update',
103 pattern=ADMIN_PREFIX + '/my_account/bookmarks/update')
104
105 config.add_route(
106 name='my_account_goto_bookmark',
107 pattern=ADMIN_PREFIX + '/my_account/bookmark/{bookmark_id}')
108
109 config.add_route(
98 110 name='my_account_perms',
99 111 pattern=ADMIN_PREFIX + '/my_account/perms')
100 112
@@ -20,29 +20,30 b''
20 20
21 21 import logging
22 22 import datetime
23 import string
23 24
24 25 import formencode
25 26 import formencode.htmlfill
27 import peppercorn
26 28 from pyramid.httpexceptions import HTTPFound
27 29 from pyramid.view import view_config
28 from pyramid.renderers import render
29 from pyramid.response import Response
30 30
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 32 from rhodecode import forms
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib import audit_logger
35 35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired, \
37 HasRepoPermissionAny, HasRepoGroupPermissionAny
37 38 from rhodecode.lib.channelstream import (
38 39 channelstream_request, ChannelstreamException)
39 40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
40 41 from rhodecode.model.auth_token import AuthTokenModel
41 42 from rhodecode.model.comment import CommentsModel
42 43 from rhodecode.model.db import (
43 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload,
44 PullRequest)
45 from rhodecode.model.forms import UserForm, UserExtraEmailForm
44 IntegrityError, joinedload,
45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 PullRequest, UserBookmark, RepoGroup)
46 47 from rhodecode.model.meta import Session
47 48 from rhodecode.model.pull_request import PullRequestModel
48 49 from rhodecode.model.scm import RepoList
@@ -392,6 +393,140 b' class MyAccountView(BaseAppView, DataGri'
392 393 @LoginRequired()
393 394 @NotAnonymous()
394 395 @view_config(
396 route_name='my_account_bookmarks', request_method='GET',
397 renderer='rhodecode:templates/admin/my_account/my_account.mako')
398 def my_account_bookmarks(self):
399 c = self.load_default_context()
400 c.active = 'bookmarks'
401 return self._get_template_context(c)
402
403 def _process_entry(self, entry, user_id):
404 position = safe_int(entry.get('position'))
405 if position is None:
406 return
407
408 # check if this is an existing entry
409 is_new = False
410 db_entry = UserBookmark().get_by_position_for_user(position, user_id)
411
412 if db_entry and str2bool(entry.get('remove')):
413 log.debug('Marked bookmark %s for deletion', db_entry)
414 Session().delete(db_entry)
415 return
416
417 if not db_entry:
418 # new
419 db_entry = UserBookmark()
420 is_new = True
421
422 should_save = False
423 default_redirect_url = ''
424
425 # save repo
426 if entry.get('bookmark_repo'):
427 repo = Repository.get(entry['bookmark_repo'])
428 perm_check = HasRepoPermissionAny(
429 'repository.read', 'repository.write', 'repository.admin')
430 if repo and perm_check(repo_name=repo.repo_name):
431 db_entry.repository = repo
432 should_save = True
433 default_redirect_url = '${repo_url}'
434 # save repo group
435 elif entry.get('bookmark_repo_group'):
436 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
437 perm_check = HasRepoGroupPermissionAny(
438 'group.read', 'group.write', 'group.admin')
439
440 if repo_group and perm_check(group_name=repo_group.group_name):
441 db_entry.repository_group = repo_group
442 should_save = True
443 default_redirect_url = '${repo_group_url}'
444 # save generic info
445 elif entry.get('title') and entry.get('redirect_url'):
446 should_save = True
447
448 if should_save:
449 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
450 # mark user and position
451 db_entry.user_id = user_id
452 db_entry.position = position
453 db_entry.title = entry.get('title')
454 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
455
456 Session().add(db_entry)
457
458 @LoginRequired()
459 @NotAnonymous()
460 @CSRFRequired()
461 @view_config(
462 route_name='my_account_bookmarks_update', request_method='POST')
463 def my_account_bookmarks_update(self):
464 _ = self.request.translate
465 c = self.load_default_context()
466 c.active = 'bookmarks'
467
468 controls = peppercorn.parse(self.request.POST.items())
469 user_id = c.user.user_id
470
471 try:
472 for entry in controls.get('bookmarks', []):
473 self._process_entry(entry, user_id)
474
475 Session().commit()
476 h.flash(_("Update Bookmarks"), category='success')
477 except IntegrityError:
478 h.flash(_("Failed to update bookmarks. "
479 "Make sure an unique position is used"), category='error')
480
481 return HTTPFound(h.route_path('my_account_bookmarks'))
482
483 @LoginRequired()
484 @NotAnonymous()
485 @view_config(
486 route_name='my_account_goto_bookmark', request_method='GET',
487 renderer='rhodecode:templates/admin/my_account/my_account.mako')
488 def my_account_goto_bookmark(self):
489
490 bookmark_id = self.request.matchdict['bookmark_id']
491 user_bookmark = UserBookmark().query()\
492 .filter(UserBookmark.user_id == self.request.user.user_id) \
493 .filter(UserBookmark.position == bookmark_id).scalar()
494
495 redirect_url = h.route_path('my_account_bookmarks')
496 if not user_bookmark:
497 raise HTTPFound(redirect_url)
498
499 if user_bookmark.repository:
500 repo_name = user_bookmark.repository.repo_name
501 base_redirect_url = h.route_path(
502 'repo_summary', repo_name=repo_name)
503 if user_bookmark.redirect_url and \
504 '${repo_url}' in user_bookmark.redirect_url:
505 redirect_url = string.Template(user_bookmark.redirect_url)\
506 .safe_substitute({'repo_url': base_redirect_url})
507 else:
508 redirect_url = base_redirect_url
509
510 elif user_bookmark.repository_group:
511 repo_group_name = user_bookmark.repository_group.group_name
512 base_redirect_url = h.route_path(
513 'repo_group_home', repo_group_name=repo_group_name)
514 if user_bookmark.redirect_url and \
515 '${repo_group_url}' in user_bookmark.redirect_url:
516 redirect_url = string.Template(user_bookmark.redirect_url)\
517 .safe_substitute({'repo_group_url': base_redirect_url})
518 else:
519 redirect_url = base_redirect_url
520
521 elif user_bookmark.redirect_url:
522 redirect_url = user_bookmark.redirect_url
523
524 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
525 raise HTTPFound(redirect_url)
526
527 @LoginRequired()
528 @NotAnonymous()
529 @view_config(
395 530 route_name='my_account_perms', request_method='GET',
396 531 renderer='rhodecode:templates/admin/my_account/my_account.mako')
397 532 def my_account_perms(self):
@@ -44,7 +44,7 b' from rhodecode.lib.exceptions import Use'
44 44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
45 45 from rhodecode.lib.utils2 import (
46 46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
47 from rhodecode.model.db import Repository, User, ChangesetComment
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
48 48 from rhodecode.model.notification import NotificationModel
49 49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
50 50
@@ -282,7 +282,7 b' def get_current_lang(request):'
282 282 return getattr(request, '_LOCALE_', request.locale_name)
283 283
284 284
285 def attach_context_attributes(context, request, user_id):
285 def attach_context_attributes(context, request, user_id=None):
286 286 """
287 287 Attach variables into template context called `c`.
288 288 """
@@ -422,7 +422,13 b' def attach_context_attributes(context, r'
422 422 context.csrf_token = auth.get_csrf_token(session=request.session)
423 423 context.backends = rhodecode.BACKENDS.keys()
424 424 context.backends.sort()
425 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
425 unread_count = 0
426 user_bookmark_list = []
427 if user_id:
428 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
429 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
430 context.unread_notifications = unread_count
431 context.bookmark_items = user_bookmark_list
426 432
427 433 # web case
428 434 if hasattr(request, 'user'):
@@ -4797,6 +4797,49 b' class UserGroupToRepoBranchPermission(Ba'
4797 4797 self.user_group_repo_to_perm, self.branch_pattern)
4798 4798
4799 4799
4800 class UserBookmark(Base, BaseModel):
4801 __tablename__ = 'user_bookmarks'
4802 __table_args__ = (
4803 UniqueConstraint('user_id', 'bookmark_repo_id'),
4804 UniqueConstraint('user_id', 'bookmark_repo_group_id'),
4805 UniqueConstraint('user_id', 'bookmark_position'),
4806 base_table_args
4807 )
4808
4809 user_bookmark_id = Column("user_bookmark_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
4810 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
4811 position = Column("bookmark_position", Integer(), nullable=False)
4812 title = Column("bookmark_title", String(255), nullable=True, unique=None, default=None)
4813 redirect_url = Column("bookmark_redirect_url", String(10240), nullable=True, unique=None, default=None)
4814 created_on = Column("created_on", DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4815
4816 bookmark_repo_id = Column("bookmark_repo_id", Integer(), ForeignKey("repositories.repo_id"), nullable=True, unique=None, default=None)
4817 bookmark_repo_group_id = Column("bookmark_repo_group_id", Integer(), ForeignKey("groups.group_id"), nullable=True, unique=None, default=None)
4818
4819 user = relationship("User")
4820
4821 repository = relationship("Repository")
4822 repository_group = relationship("RepoGroup")
4823
4824 @classmethod
4825 def get_by_position_for_user(cls, position, user_id):
4826 return cls.query() \
4827 .filter(UserBookmark.user_id == user_id) \
4828 .filter(UserBookmark.position == position).scalar()
4829
4830 @classmethod
4831 def get_bookmarks_for_user(cls, user_id):
4832 return cls.query() \
4833 .filter(UserBookmark.user_id == user_id) \
4834 .options(joinedload(UserBookmark.repository)) \
4835 .options(joinedload(UserBookmark.repository_group)) \
4836 .order_by(UserBookmark.position.asc()) \
4837 .all()
4838
4839 def __unicode__(self):
4840 return u'<UserBookmark(%d @ %r)>' % (self.position, self.redirect_url)
4841
4842
4800 4843 class DbMigrateVersion(Base, BaseModel):
4801 4844 __tablename__ = 'db_migrate_version'
4802 4845 __table_args__ = (
@@ -200,7 +200,27 b''
200 200 .user-menu.submenu {
201 201 right: 0;
202 202 left: auto;
203 min-width: 290px;
203 204 }
205
206
207 .user-menu {
208 .bookmark-items {
209 padding: 4px 2px;
210 color: @grey3;
211 border-bottom: @grey3;
212
213 a {
214 padding: 0 !important;
215 color: @rcblue !important;
216 }
217 }
218 a.bookmark-item {
219 color: @rcblue !important;
220 }
221 }
222
223
204 224 #quick_login {
205 225 left: auto;
206 226 right: 0;
@@ -335,6 +335,7 b''
335 335 }
336 336 }
337 337 }
338
338 339 }
339 340
340 341
@@ -172,7 +172,9 b' table.dataTable {'
172 172 &.td-buttons {
173 173 padding: .3em 0;
174 174 }
175
175 &.td-align-top {
176 vertical-align: text-top
177 }
176 178 &.td-action {
177 179 // this is for the remove/delete/edit buttons
178 180 padding-right: 0;
@@ -526,6 +526,10 b' address {'
526 526 font-family: @text-regular;
527 527 }
528 528
529 .help-block-inline {
530 margin: 0;
531 }
532
529 533 // help block text
530 534 .help-block {
531 535 display: block;
@@ -51,6 +51,38 b' function setRCMouseBindings(repoName, re'
51 51 Mousetrap.bind(['g G'], function(e) {
52 52 window.location = pyroutes.url('gists_show', {'public': 1});
53 53 });
54
55 Mousetrap.bind(['g 0'], function(e) {
56 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 0});
57 });
58 Mousetrap.bind(['g 1'], function(e) {
59 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 1});
60 });
61 Mousetrap.bind(['g 2'], function(e) {
62 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 2});
63 });
64 Mousetrap.bind(['g 3'], function(e) {
65 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 3});
66 });
67 Mousetrap.bind(['g 4'], function(e) {
68 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 4});
69 });
70 Mousetrap.bind(['g 5'], function(e) {
71 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 5});
72 });
73 Mousetrap.bind(['g 6'], function(e) {
74 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 6});
75 });
76 Mousetrap.bind(['g 7'], function(e) {
77 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 7});
78 });
79 Mousetrap.bind(['g 8'], function(e) {
80 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 8});
81 });
82 Mousetrap.bind(['g 9'], function(e) {
83 window.location = pyroutes.url('my_account_goto_bookmark', {'bookmark_id': 9});
84 });
85
54 86 Mousetrap.bind(['n g'], function(e) {
55 87 window.location = pyroutes.url('gists_new');
56 88 });
@@ -58,7 +90,7 b' function setRCMouseBindings(repoName, re'
58 90 window.location = pyroutes.url('repo_new');
59 91 });
60 92
61 if (repoName && repoName != '') {
93 if (repoName && repoName !== '') {
62 94 // nav in repo context
63 95 Mousetrap.bind(['g s'], function(e) {
64 96 window.location = pyroutes.url(
@@ -143,6 +143,7 b' function registerRCRoutes() {'
143 143 pyroutes.register('user_autocomplete_data', '/_users', []);
144 144 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
145 145 pyroutes.register('repo_list_data', '/_repos', []);
146 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
146 147 pyroutes.register('goto_switcher_data', '/_goto_data', []);
147 148 pyroutes.register('markup_preview', '/_markup_preview', []);
148 149 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
@@ -297,6 +298,9 b' function registerRCRoutes() {'
297 298 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
298 299 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
299 300 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
301 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
302 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
303 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
300 304 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
301 305 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
302 306 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
@@ -28,6 +28,7 b''
28 28 <ul class="nav nav-pills nav-stacked">
29 29 <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
30 30 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
31 <li class="${'active' if c.active=='bookmarks' else ''}"><a href="${h.route_path('my_account_bookmarks')}">${_('Bookmarks')}</a></li>
31 32 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
32 33 <li class="${'active' if c.active in ['ssh_keys', 'ssh_keys_generate'] else ''}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
33 34 <li class="${'active' if c.active=='user_group_membership' else ''}"><a href="${h.route_path('my_account_user_group_membership')}">${_('User Group Membership')}</a></li>
@@ -52,7 +52,7 b''
52 52 <p class="server-instance" style="display:${sid}">
53 53 ## display hidden instance ID if specially defined
54 54 % if c.rhodecode_instanceid:
55 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
55 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
56 56 % endif
57 57 </p>
58 58 </div>
@@ -331,6 +331,50 b''
331 331 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
332 332 % endif
333 333 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
334 ## bookmark-items
335 <li class="bookmark-items">
336 ${_('Bookmarks')}
337 <div class="pull-right">
338 <a href="${h.route_path('my_account_bookmarks')}">${_('Manage')}</a>
339 </div>
340 </li>
341 % if not c.bookmark_items:
342 <li>
343 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
344 </li>
345 % endif
346 % for item in c.bookmark_items:
347 <li>
348 % if item.repository:
349 <div>
350 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
351 ${item.position}
352 % if item.repository.repo_type == 'hg':
353 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
354 % elif item.repository.repo_type == 'git':
355 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
356 % elif item.repository.repo_type == 'svn':
357 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
358 % endif
359 ${(item.title or h.shorter(item.repository.repo_name, 30))}
360 </a>
361 </div>
362 % elif item.repository_group:
363 <div>
364 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
365 ${item.position}
366 <i class="icon-folder-close" title="${_('Repository group')}" style="font-size: 16px"></i>
367 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
368 </a>
369 </div>
370 % else:
371 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
372 ${item.position}
373 ${item.title}
374 </a>
375 % endif
376 </li>
377 % endfor
334 378
335 379 <li class="logout">
336 380 ${h.secure_form(h.route_path('logout'), request=request)}
@@ -483,6 +527,26 b' commit:efced4, to search for commits'
483 527 }(result, escapeMarkup);
484 528 };
485 529
530 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
531 return function(data, escapeMarkup) {
532 if (!data.repo_group_id){
533 return data.text; // optgroup text Repositories
534 }
535
536 var tmpl = '';
537 var repoGroupName = data['text'];
538
539 if(data){
540
541 tmpl += '<i class="icon-folder-close"></i> ';
542
543 }
544 tmpl += escapeMarkup(repoGroupName);
545 return tmpl;
546
547 }(result, escapeMarkup);
548 };
549
486 550
487 551 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
488 552
@@ -610,6 +674,7 b' commit:efced4, to search for commits'
610 674 ('g h', 'Goto home page'),
611 675 ('g g', 'Goto my private gists page'),
612 676 ('g G', 'Goto my public gists page'),
677 ('g 0-9', 'Goto bookmarked items from 0-9'),
613 678 ('n r', 'New repository page'),
614 679 ('n g', 'New gist page'),
615 680 ]
@@ -88,9 +88,9 b''
88 88 %endif
89 89
90 90 ##PRIVATE/PUBLIC
91 %if private and c.visual.show_private_icon:
91 %if private is True and c.visual.show_private_icon:
92 92 <i class="icon-lock" title="${_('Private repository')}"></i>
93 %elif not private and c.visual.show_public_icon:
93 %elif private is False and c.visual.show_public_icon:
94 94 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
95 95 %else:
96 96 <span></span>
General Comments 0
You need to be logged in to leave comments. Login now