##// 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
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 EXTENSIONS = {}
45 EXTENSIONS = {}
46
46
47 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
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 __platform__ = platform.system()
49 __platform__ = platform.system()
50 __license__ = 'AGPLv3, and Commercial License'
50 __license__ = 'AGPLv3, and Commercial License'
51 __author__ = 'RhodeCode GmbH'
51 __author__ = 'RhodeCode GmbH'
@@ -56,6 +56,10 b' def includeme(config):'
56 pattern='/_repos')
56 pattern='/_repos')
57
57
58 config.add_route(
58 config.add_route(
59 name='repo_group_list_data',
60 pattern='/_repo_groups')
61
62 config.add_route(
59 name='goto_switcher_data',
63 name='goto_switcher_data',
60 pattern='/_goto_data')
64 pattern='/_goto_data')
61
65
@@ -172,7 +172,9 b' class HomeView(BaseAppView):'
172 'id': obj.group_name,
172 'id': obj.group_name,
173 'value': org_query,
173 'value': org_query,
174 'value_display': obj.group_name,
174 'value_display': obj.group_name,
175 'text': obj.group_name,
175 'type': 'repo_group',
176 'type': 'repo_group',
177 'repo_group_id': obj.group_id,
176 'url': h.route_path(
178 'url': h.route_path(
177 'repo_group_home', repo_group_name=obj.group_name)
179 'repo_group_home', repo_group_name=obj.group_name)
178 }
180 }
@@ -302,6 +304,33 b' class HomeView(BaseAppView):'
302 }
304 }
303 return data
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 def _get_default_search_queries(self, search_context, searcher, query):
334 def _get_default_search_queries(self, search_context, searcher, query):
306 if not searcher:
335 if not searcher:
307 return []
336 return []
@@ -95,6 +95,18 b' def includeme(config):'
95 pattern=ADMIN_PREFIX + '/my_account/watched')
95 pattern=ADMIN_PREFIX + '/my_account/watched')
96
96
97 config.add_route(
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 name='my_account_perms',
110 name='my_account_perms',
99 pattern=ADMIN_PREFIX + '/my_account/perms')
111 pattern=ADMIN_PREFIX + '/my_account/perms')
100
112
@@ -20,29 +20,30 b''
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import string
23
24
24 import formencode
25 import formencode
25 import formencode.htmlfill
26 import formencode.htmlfill
27 import peppercorn
26 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
29 from pyramid.view import view_config
28 from pyramid.renderers import render
29 from pyramid.response import Response
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode import forms
32 from rhodecode import forms
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib import audit_logger
34 from rhodecode.lib import audit_logger
35 from rhodecode.lib.ext_json import json
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 from rhodecode.lib.channelstream import (
38 from rhodecode.lib.channelstream import (
38 channelstream_request, ChannelstreamException)
39 channelstream_request, ChannelstreamException)
39 from rhodecode.lib.utils2 import safe_int, md5, str2bool
40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
40 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.comment import CommentsModel
42 from rhodecode.model.comment import CommentsModel
42 from rhodecode.model.db import (
43 from rhodecode.model.db import (
43 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload,
44 IntegrityError, joinedload,
44 PullRequest)
45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
45 from rhodecode.model.forms import UserForm, UserExtraEmailForm
46 PullRequest, UserBookmark, RepoGroup)
46 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
47 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.scm import RepoList
49 from rhodecode.model.scm import RepoList
@@ -392,6 +393,140 b' class MyAccountView(BaseAppView, DataGri'
392 @LoginRequired()
393 @LoginRequired()
393 @NotAnonymous()
394 @NotAnonymous()
394 @view_config(
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 route_name='my_account_perms', request_method='GET',
530 route_name='my_account_perms', request_method='GET',
396 renderer='rhodecode:templates/admin/my_account/my_account.mako')
531 renderer='rhodecode:templates/admin/my_account/my_account.mako')
397 def my_account_perms(self):
532 def my_account_perms(self):
@@ -44,7 +44,7 b' from rhodecode.lib.exceptions import Use'
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
45 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
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 from rhodecode.model.notification import NotificationModel
48 from rhodecode.model.notification import NotificationModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
50
50
@@ -282,7 +282,7 b' def get_current_lang(request):'
282 return getattr(request, '_LOCALE_', request.locale_name)
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 Attach variables into template context called `c`.
287 Attach variables into template context called `c`.
288 """
288 """
@@ -422,7 +422,13 b' def attach_context_attributes(context, r'
422 context.csrf_token = auth.get_csrf_token(session=request.session)
422 context.csrf_token = auth.get_csrf_token(session=request.session)
423 context.backends = rhodecode.BACKENDS.keys()
423 context.backends = rhodecode.BACKENDS.keys()
424 context.backends.sort()
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 # web case
433 # web case
428 if hasattr(request, 'user'):
434 if hasattr(request, 'user'):
@@ -4797,6 +4797,49 b' class UserGroupToRepoBranchPermission(Ba'
4797 self.user_group_repo_to_perm, self.branch_pattern)
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 class DbMigrateVersion(Base, BaseModel):
4843 class DbMigrateVersion(Base, BaseModel):
4801 __tablename__ = 'db_migrate_version'
4844 __tablename__ = 'db_migrate_version'
4802 __table_args__ = (
4845 __table_args__ = (
@@ -200,7 +200,27 b''
200 .user-menu.submenu {
200 .user-menu.submenu {
201 right: 0;
201 right: 0;
202 left: auto;
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 #quick_login {
224 #quick_login {
205 left: auto;
225 left: auto;
206 right: 0;
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 &.td-buttons {
172 &.td-buttons {
173 padding: .3em 0;
173 padding: .3em 0;
174 }
174 }
175
175 &.td-align-top {
176 vertical-align: text-top
177 }
176 &.td-action {
178 &.td-action {
177 // this is for the remove/delete/edit buttons
179 // this is for the remove/delete/edit buttons
178 padding-right: 0;
180 padding-right: 0;
@@ -526,6 +526,10 b' address {'
526 font-family: @text-regular;
526 font-family: @text-regular;
527 }
527 }
528
528
529 .help-block-inline {
530 margin: 0;
531 }
532
529 // help block text
533 // help block text
530 .help-block {
534 .help-block {
531 display: block;
535 display: block;
@@ -51,6 +51,38 b' function setRCMouseBindings(repoName, re'
51 Mousetrap.bind(['g G'], function(e) {
51 Mousetrap.bind(['g G'], function(e) {
52 window.location = pyroutes.url('gists_show', {'public': 1});
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 Mousetrap.bind(['n g'], function(e) {
86 Mousetrap.bind(['n g'], function(e) {
55 window.location = pyroutes.url('gists_new');
87 window.location = pyroutes.url('gists_new');
56 });
88 });
@@ -58,7 +90,7 b' function setRCMouseBindings(repoName, re'
58 window.location = pyroutes.url('repo_new');
90 window.location = pyroutes.url('repo_new');
59 });
91 });
60
92
61 if (repoName && repoName != '') {
93 if (repoName && repoName !== '') {
62 // nav in repo context
94 // nav in repo context
63 Mousetrap.bind(['g s'], function(e) {
95 Mousetrap.bind(['g s'], function(e) {
64 window.location = pyroutes.url(
96 window.location = pyroutes.url(
@@ -143,6 +143,7 b' function registerRCRoutes() {'
143 pyroutes.register('user_autocomplete_data', '/_users', []);
143 pyroutes.register('user_autocomplete_data', '/_users', []);
144 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
144 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
145 pyroutes.register('repo_list_data', '/_repos', []);
145 pyroutes.register('repo_list_data', '/_repos', []);
146 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
146 pyroutes.register('goto_switcher_data', '/_goto_data', []);
147 pyroutes.register('goto_switcher_data', '/_goto_data', []);
147 pyroutes.register('markup_preview', '/_markup_preview', []);
148 pyroutes.register('markup_preview', '/_markup_preview', []);
148 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
149 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
@@ -297,6 +298,9 b' function registerRCRoutes() {'
297 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
298 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
298 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
299 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
299 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
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 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
304 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
301 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
305 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
302 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
306 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
@@ -28,6 +28,7 b''
28 <ul class="nav nav-pills nav-stacked">
28 <ul class="nav nav-pills nav-stacked">
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>
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 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
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 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
32 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
32 <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 <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 <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>
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 <p class="server-instance" style="display:${sid}">
52 <p class="server-instance" style="display:${sid}">
53 ## display hidden instance ID if specially defined
53 ## display hidden instance ID if specially defined
54 % if c.rhodecode_instanceid:
54 % if c.rhodecode_instanceid:
55 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
55 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
56 % endif
56 % endif
57 </p>
57 </p>
58 </div>
58 </div>
@@ -331,6 +331,50 b''
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>
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 % endif
332 % endif
333 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
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 <li class="logout">
379 <li class="logout">
336 ${h.secure_form(h.route_path('logout'), request=request)}
380 ${h.secure_form(h.route_path('logout'), request=request)}
@@ -483,6 +527,26 b' commit:efced4, to search for commits'
483 }(result, escapeMarkup);
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 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
551 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
488
552
@@ -610,6 +674,7 b' commit:efced4, to search for commits'
610 ('g h', 'Goto home page'),
674 ('g h', 'Goto home page'),
611 ('g g', 'Goto my private gists page'),
675 ('g g', 'Goto my private gists page'),
612 ('g G', 'Goto my public gists page'),
676 ('g G', 'Goto my public gists page'),
677 ('g 0-9', 'Goto bookmarked items from 0-9'),
613 ('n r', 'New repository page'),
678 ('n r', 'New repository page'),
614 ('n g', 'New gist page'),
679 ('n g', 'New gist page'),
615 ]
680 ]
@@ -88,9 +88,9 b''
88 %endif
88 %endif
89
89
90 ##PRIVATE/PUBLIC
90 ##PRIVATE/PUBLIC
91 %if private and c.visual.show_private_icon:
91 %if private is True and c.visual.show_private_icon:
92 <i class="icon-lock" title="${_('Private repository')}"></i>
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 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
94 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
95 %else:
95 %else:
96 <span></span>
96 <span></span>
General Comments 0
You need to be logged in to leave comments. Login now