##// END OF EJS Templates
Notification system improvements...
marcink -
r1712:cac5109a beta
parent child Browse files
Show More
@@ -0,0 +1,84 b''
1 import logging
2
3 from pylons import tmpl_context as c
4
5 from rhodecode.lib.base import BaseController, render
6 from rhodecode.model.db import Notification
7
8 from rhodecode.model.notification import NotificationModel
9 from rhodecode.lib.auth import LoginRequired
10 from rhodecode.lib import helpers as h
11
12 log = logging.getLogger(__name__)
13
14 class NotificationsController(BaseController):
15 """REST Controller styled on the Atom Publishing Protocol"""
16 # To properly map this controller, ensure your config/routing.py
17 # file has a resource setup:
18 # map.resource('notification', 'notifications', controller='_admin/notifications',
19 # path_prefix='/_admin', name_prefix='_admin_')
20
21 @LoginRequired()
22 def __before__(self):
23 super(NotificationsController, self).__before__()
24
25
26 def index(self, format='html'):
27 """GET /_admin/notifications: All items in the collection"""
28 # url('notifications')
29 c.user = self.rhodecode_user
30 c.notifications = NotificationModel()\
31 .get_for_user(self.rhodecode_user.user_id)
32 return render('admin/notifications/notifications.html')
33
34 def create(self):
35 """POST /_admin/notifications: Create a new item"""
36 # url('notifications')
37
38 def new(self, format='html'):
39 """GET /_admin/notifications/new: Form to create a new item"""
40 # url('new_notification')
41
42 def update(self, notification_id):
43 """PUT /_admin/notifications/id: Update an existing item"""
44 # Forms posted to this method should contain a hidden field:
45 # <input type="hidden" name="_method" value="PUT" />
46 # Or using helpers:
47 # h.form(url('notification', notification_id=ID),
48 # method='put')
49 # url('notification', notification_id=ID)
50
51 def delete(self, notification_id):
52 """DELETE /_admin/notifications/id: Delete an existing item"""
53 # Forms posted to this method should contain a hidden field:
54 # <input type="hidden" name="_method" value="DELETE" />
55 # Or using helpers:
56 # h.form(url('notification', notification_id=ID),
57 # method='delete')
58 # url('notification', notification_id=ID)
59
60 no = Notification.get(notification_id)
61 owner = lambda: no.notifications_to_users.user.user_id == c.rhodecode_user.user_id
62 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
63 NotificationModel().delete(notification_id)
64 return 'ok'
65 return 'fail'
66
67 def show(self, notification_id, format='html'):
68 """GET /_admin/notifications/id: Show a specific item"""
69 # url('notification', notification_id=ID)
70 c.user = self.rhodecode_user
71 c.notification = Notification.get(notification_id)
72
73 unotification = NotificationModel()\
74 .get_user_notification(c.user.user_id,
75 c.notification)
76
77 if unotification.read is False:
78 unotification.mark_as_read()
79
80 return render('admin/notifications/show_notification.html')
81
82 def edit(self, notification_id, format='html'):
83 """GET /_admin/notifications/id/edit: Form to edit an existing item"""
84 # url('edit_notification', notification_id=ID)
@@ -0,0 +1,51 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
3
4 <%def name="title()">
5 ${_('Show notification')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 </%def>
7
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Notifications'),h.url('notifications'))}
10 &raquo;
11 ${_('Show notification')}
12 </%def>
13
14 <%def name="page_nav()">
15 ${self.menu('admin')}
16 </%def>
17
18 <%def name="main()">
19 <div class="box">
20 <!-- box / title -->
21 <div class="title">
22 ${self.breadcrumbs()}
23 <ul class="links">
24 <li>
25 <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
26 </li>
27 </ul>
28 </div>
29 <div class="table">
30 <div class="notification-header">
31 <div class="gravatar">
32 <img alt="gravatar" src="${h.gravatar_url(h.email(c.notification.created_by_user.email),24)}"/>
33 </div>
34 <div class="desc">
35 ${c.notification.description}
36 </div>
37 <div class="delete-notifications">
38 <span id="${c.notification.notification_id}" class="delete_icon action"></span>
39 </div>
40 </div>
41 <div>${h.rst(c.notification.body)}</div>
42 </div>
43 </div>
44 <script type="text/javascript">
45 var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
46 YUE.on(YUQ('.delete-notification'),'click',function(e){
47 var notification_id = e.currentTarget.id;
48 deleteNotification(url,notification_id)
49 })
50 </script>
51 </%def>
@@ -0,0 +1,117 b''
1 from rhodecode.tests import *
2 from rhodecode.model.db import Notification, User, UserNotification
3
4 from rhodecode.model.user import UserModel
5 from rhodecode.model.notification import NotificationModel
6
7 class TestNotificationsController(TestController):
8
9 def test_index(self):
10 self.log_user()
11
12
13 u1 = UserModel().create_or_update(username='u1', password='qweqwe',
14 email='u1@rhodecode.org',
15 name='u1', lastname='u1').user_id
16 u2 = UserModel().create_or_update(username='u2', password='qweqwe',
17 email='u2@rhodecode.org',
18 name='u2', lastname='u2').user_id
19
20 response = self.app.get(url('notifications'))
21 self.assertTrue('''<div class="table">No notifications here yet</div>'''
22 in response.body)
23
24 cur_user = self._get_logged_user()
25
26 NotificationModel().create(created_by=u1, subject=u'test',
27 body=u'notification_1',
28 recipients=[cur_user])
29 response = self.app.get(url('notifications'))
30
31 self.assertTrue(u'notification_1' in response.body)
32
33 User.delete(u1)
34 User.delete(u2)
35
36 # def test_index_as_xml(self):
37 # response = self.app.get(url('formatted_notifications', format='xml'))
38 #
39 # def test_create(self):
40 # response = self.app.post(url('notifications'))
41 #
42 # def test_new(self):
43 # response = self.app.get(url('new_notification'))
44 #
45 # def test_new_as_xml(self):
46 # response = self.app.get(url('formatted_new_notification', format='xml'))
47 #
48 # def test_update(self):
49 # response = self.app.put(url('notification', notification_id=1))
50 #
51 # def test_update_browser_fakeout(self):
52 # response = self.app.post(url('notification', notification_id=1), params=dict(_method='put'))
53
54 def test_delete(self):
55 self.log_user()
56 cur_user = self._get_logged_user()
57
58 u1 = UserModel().create_or_update(username='u1', password='qweqwe',
59 email='u1@rhodecode.org',
60 name='u1', lastname='u1')
61 u2 = UserModel().create_or_update(username='u2', password='qweqwe',
62 email='u2@rhodecode.org',
63 name='u2', lastname='u2')
64
65 # make two notifications
66 notification = NotificationModel().create(created_by=cur_user,
67 subject=u'test',
68 body=u'hi there',
69 recipients=[cur_user, u1, u2])
70
71 u1 = User.get(u1.user_id)
72 u2 = User.get(u2.user_id)
73
74 # check DB
75 self.assertEqual(u1.notifications, [notification])
76 self.assertEqual(u2.notifications, [notification])
77 cur_usr_id = cur_user.user_id
78 response = self.app.delete(url('notification',
79 notification_id=cur_usr_id))
80
81 cur_user = self._get_logged_user()
82 self.assertEqual(cur_user.notifications, [])
83
84 User.delete(u1.user_id)
85 User.delete(u2.user_id)
86
87
88 # def test_delete_browser_fakeout(self):
89 # response = self.app.post(url('notification', notification_id=1), params=dict(_method='delete'))
90
91 def test_show(self):
92 self.log_user()
93 cur_user = self._get_logged_user()
94 u1 = UserModel().create_or_update(username='u1', password='qweqwe',
95 email='u1@rhodecode.org',
96 name='u1', lastname='u1')
97 u2 = UserModel().create_or_update(username='u2', password='qweqwe',
98 email='u2@rhodecode.org',
99 name='u2', lastname='u2')
100
101 notification = NotificationModel().create(created_by=cur_user,
102 subject='test',
103 body='hi there',
104 recipients=[cur_user, u1, u2])
105
106 response = self.app.get(url('notification',
107 notification_id=notification.notification_id))
108
109 # def test_show_as_xml(self):
110 # response = self.app.get(url('formatted_notification', notification_id=1, format='xml'))
111 #
112 # def test_edit(self):
113 # response = self.app.get(url('edit_notification', notification_id=1))
114 #
115 # def test_edit_as_xml(self):
116 # response = self.app.get(url('formatted_edit_notification', notification_id=1, format='xml'))
117
@@ -1,456 +1,483 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10
10
11
11
12 # prefix for non repository related links needs to be prefixed with `/`
12 # prefix for non repository related links needs to be prefixed with `/`
13 ADMIN_PREFIX = '/_admin'
13 ADMIN_PREFIX = '/_admin'
14
14
15
15
16 def make_map(config):
16 def make_map(config):
17 """Create, configure and return the routes Mapper"""
17 """Create, configure and return the routes Mapper"""
18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
19 always_scan=config['debug'])
19 always_scan=config['debug'])
20 rmap.minimization = False
20 rmap.minimization = False
21 rmap.explicit = False
21 rmap.explicit = False
22
22
23 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repo
24 from rhodecode.lib.utils import is_valid_repos_group
24 from rhodecode.lib.utils import is_valid_repos_group
25
25
26 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
27 """
27 """
28 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
29
29
30 :param environ:
30 :param environ:
31 :param match_dict:
31 :param match_dict:
32 """
32 """
33
33
34 repo_name = match_dict.get('repo_name')
34 repo_name = match_dict.get('repo_name')
35 return is_valid_repo(repo_name, config['base_path'])
35 return is_valid_repo(repo_name, config['base_path'])
36
36
37 def check_group(environ, match_dict):
37 def check_group(environ, match_dict):
38 """
38 """
39 check for valid repositories group for proper 404 handling
39 check for valid repositories group for proper 404 handling
40
40
41 :param environ:
41 :param environ:
42 :param match_dict:
42 :param match_dict:
43 """
43 """
44 repos_group_name = match_dict.get('group_name')
44 repos_group_name = match_dict.get('group_name')
45
45
46 return is_valid_repos_group(repos_group_name, config['base_path'])
46 return is_valid_repos_group(repos_group_name, config['base_path'])
47
47
48
48
49 def check_int(environ, match_dict):
49 def check_int(environ, match_dict):
50 return match_dict.get('id').isdigit()
50 return match_dict.get('id').isdigit()
51
51
52 # The ErrorController route (handles 404/500 error pages); it should
52 # The ErrorController route (handles 404/500 error pages); it should
53 # likely stay at the top, ensuring it can always be resolved
53 # likely stay at the top, ensuring it can always be resolved
54 rmap.connect('/error/{action}', controller='error')
54 rmap.connect('/error/{action}', controller='error')
55 rmap.connect('/error/{action}/{id}', controller='error')
55 rmap.connect('/error/{action}/{id}', controller='error')
56
56
57 #==========================================================================
57 #==========================================================================
58 # CUSTOM ROUTES HERE
58 # CUSTOM ROUTES HERE
59 #==========================================================================
59 #==========================================================================
60
60
61 #MAIN PAGE
61 #MAIN PAGE
62 rmap.connect('home', '/', controller='home', action='index')
62 rmap.connect('home', '/', controller='home', action='index')
63 rmap.connect('repo_switcher', '/repos', controller='home',
63 rmap.connect('repo_switcher', '/repos', controller='home',
64 action='repo_switcher')
64 action='repo_switcher')
65 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
65 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
66 controller='home',action='branch_tag_switcher')
66 controller='home', action='branch_tag_switcher')
67 rmap.connect('bugtracker',
67 rmap.connect('bugtracker',
68 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
68 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
69 _static=True)
69 _static=True)
70 rmap.connect('rst_help',
70 rmap.connect('rst_help',
71 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
71 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
72 _static=True)
72 _static=True)
73 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
73 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
74
74
75 #ADMIN REPOSITORY REST ROUTES
75 #ADMIN REPOSITORY REST ROUTES
76 with rmap.submapper(path_prefix=ADMIN_PREFIX,
76 with rmap.submapper(path_prefix=ADMIN_PREFIX,
77 controller='admin/repos') as m:
77 controller='admin/repos') as m:
78 m.connect("repos", "/repos",
78 m.connect("repos", "/repos",
79 action="create", conditions=dict(method=["POST"]))
79 action="create", conditions=dict(method=["POST"]))
80 m.connect("repos", "/repos",
80 m.connect("repos", "/repos",
81 action="index", conditions=dict(method=["GET"]))
81 action="index", conditions=dict(method=["GET"]))
82 m.connect("formatted_repos", "/repos.{format}",
82 m.connect("formatted_repos", "/repos.{format}",
83 action="index",
83 action="index",
84 conditions=dict(method=["GET"]))
84 conditions=dict(method=["GET"]))
85 m.connect("new_repo", "/repos/new",
85 m.connect("new_repo", "/repos/new",
86 action="new", conditions=dict(method=["GET"]))
86 action="new", conditions=dict(method=["GET"]))
87 m.connect("formatted_new_repo", "/repos/new.{format}",
87 m.connect("formatted_new_repo", "/repos/new.{format}",
88 action="new", conditions=dict(method=["GET"]))
88 action="new", conditions=dict(method=["GET"]))
89 m.connect("/repos/{repo_name:.*}",
89 m.connect("/repos/{repo_name:.*}",
90 action="update", conditions=dict(method=["PUT"],
90 action="update", conditions=dict(method=["PUT"],
91 function=check_repo))
91 function=check_repo))
92 m.connect("/repos/{repo_name:.*}",
92 m.connect("/repos/{repo_name:.*}",
93 action="delete", conditions=dict(method=["DELETE"],
93 action="delete", conditions=dict(method=["DELETE"],
94 function=check_repo))
94 function=check_repo))
95 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
95 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
96 action="edit", conditions=dict(method=["GET"],
96 action="edit", conditions=dict(method=["GET"],
97 function=check_repo))
97 function=check_repo))
98 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
98 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
99 action="edit", conditions=dict(method=["GET"],
99 action="edit", conditions=dict(method=["GET"],
100 function=check_repo))
100 function=check_repo))
101 m.connect("repo", "/repos/{repo_name:.*}",
101 m.connect("repo", "/repos/{repo_name:.*}",
102 action="show", conditions=dict(method=["GET"],
102 action="show", conditions=dict(method=["GET"],
103 function=check_repo))
103 function=check_repo))
104 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
104 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
105 action="show", conditions=dict(method=["GET"],
105 action="show", conditions=dict(method=["GET"],
106 function=check_repo))
106 function=check_repo))
107 #ajax delete repo perm user
107 #ajax delete repo perm user
108 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
108 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
109 action="delete_perm_user", conditions=dict(method=["DELETE"],
109 action="delete_perm_user", conditions=dict(method=["DELETE"],
110 function=check_repo))
110 function=check_repo))
111 #ajax delete repo perm users_group
111 #ajax delete repo perm users_group
112 m.connect('delete_repo_users_group',
112 m.connect('delete_repo_users_group',
113 "/repos_delete_users_group/{repo_name:.*}",
113 "/repos_delete_users_group/{repo_name:.*}",
114 action="delete_perm_users_group",
114 action="delete_perm_users_group",
115 conditions=dict(method=["DELETE"], function=check_repo))
115 conditions=dict(method=["DELETE"], function=check_repo))
116
116
117 #settings actions
117 #settings actions
118 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
118 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
119 action="repo_stats", conditions=dict(method=["DELETE"],
119 action="repo_stats", conditions=dict(method=["DELETE"],
120 function=check_repo))
120 function=check_repo))
121 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
121 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
122 action="repo_cache", conditions=dict(method=["DELETE"],
122 action="repo_cache", conditions=dict(method=["DELETE"],
123 function=check_repo))
123 function=check_repo))
124 m.connect('repo_public_journal',
124 m.connect('repo_public_journal',
125 "/repos_public_journal/{repo_name:.*}",
125 "/repos_public_journal/{repo_name:.*}",
126 action="repo_public_journal", conditions=dict(method=["PUT"],
126 action="repo_public_journal", conditions=dict(method=["PUT"],
127 function=check_repo))
127 function=check_repo))
128 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
128 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
129 action="repo_pull", conditions=dict(method=["PUT"],
129 action="repo_pull", conditions=dict(method=["PUT"],
130 function=check_repo))
130 function=check_repo))
131
131
132 with rmap.submapper(path_prefix=ADMIN_PREFIX,
132 with rmap.submapper(path_prefix=ADMIN_PREFIX,
133 controller='admin/repos_groups') as m:
133 controller='admin/repos_groups') as m:
134 m.connect("repos_groups", "/repos_groups",
134 m.connect("repos_groups", "/repos_groups",
135 action="create", conditions=dict(method=["POST"]))
135 action="create", conditions=dict(method=["POST"]))
136 m.connect("repos_groups", "/repos_groups",
136 m.connect("repos_groups", "/repos_groups",
137 action="index", conditions=dict(method=["GET"]))
137 action="index", conditions=dict(method=["GET"]))
138 m.connect("formatted_repos_groups", "/repos_groups.{format}",
138 m.connect("formatted_repos_groups", "/repos_groups.{format}",
139 action="index", conditions=dict(method=["GET"]))
139 action="index", conditions=dict(method=["GET"]))
140 m.connect("new_repos_group", "/repos_groups/new",
140 m.connect("new_repos_group", "/repos_groups/new",
141 action="new", conditions=dict(method=["GET"]))
141 action="new", conditions=dict(method=["GET"]))
142 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
142 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
143 action="new", conditions=dict(method=["GET"]))
143 action="new", conditions=dict(method=["GET"]))
144 m.connect("update_repos_group", "/repos_groups/{id}",
144 m.connect("update_repos_group", "/repos_groups/{id}",
145 action="update", conditions=dict(method=["PUT"],
145 action="update", conditions=dict(method=["PUT"],
146 function=check_int))
146 function=check_int))
147 m.connect("delete_repos_group", "/repos_groups/{id}",
147 m.connect("delete_repos_group", "/repos_groups/{id}",
148 action="delete", conditions=dict(method=["DELETE"],
148 action="delete", conditions=dict(method=["DELETE"],
149 function=check_int))
149 function=check_int))
150 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
150 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
151 action="edit", conditions=dict(method=["GET"],
151 action="edit", conditions=dict(method=["GET"],
152 function=check_int))
152 function=check_int))
153 m.connect("formatted_edit_repos_group",
153 m.connect("formatted_edit_repos_group",
154 "/repos_groups/{id}.{format}/edit",
154 "/repos_groups/{id}.{format}/edit",
155 action="edit", conditions=dict(method=["GET"],
155 action="edit", conditions=dict(method=["GET"],
156 function=check_int))
156 function=check_int))
157 m.connect("repos_group", "/repos_groups/{id}",
157 m.connect("repos_group", "/repos_groups/{id}",
158 action="show", conditions=dict(method=["GET"],
158 action="show", conditions=dict(method=["GET"],
159 function=check_int))
159 function=check_int))
160 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
160 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
161 action="show", conditions=dict(method=["GET"],
161 action="show", conditions=dict(method=["GET"],
162 function=check_int))
162 function=check_int))
163
163
164 #ADMIN USER REST ROUTES
164 #ADMIN USER REST ROUTES
165 with rmap.submapper(path_prefix=ADMIN_PREFIX,
165 with rmap.submapper(path_prefix=ADMIN_PREFIX,
166 controller='admin/users') as m:
166 controller='admin/users') as m:
167 m.connect("users", "/users",
167 m.connect("users", "/users",
168 action="create", conditions=dict(method=["POST"]))
168 action="create", conditions=dict(method=["POST"]))
169 m.connect("users", "/users",
169 m.connect("users", "/users",
170 action="index", conditions=dict(method=["GET"]))
170 action="index", conditions=dict(method=["GET"]))
171 m.connect("formatted_users", "/users.{format}",
171 m.connect("formatted_users", "/users.{format}",
172 action="index", conditions=dict(method=["GET"]))
172 action="index", conditions=dict(method=["GET"]))
173 m.connect("new_user", "/users/new",
173 m.connect("new_user", "/users/new",
174 action="new", conditions=dict(method=["GET"]))
174 action="new", conditions=dict(method=["GET"]))
175 m.connect("formatted_new_user", "/users/new.{format}",
175 m.connect("formatted_new_user", "/users/new.{format}",
176 action="new", conditions=dict(method=["GET"]))
176 action="new", conditions=dict(method=["GET"]))
177 m.connect("update_user", "/users/{id}",
177 m.connect("update_user", "/users/{id}",
178 action="update", conditions=dict(method=["PUT"]))
178 action="update", conditions=dict(method=["PUT"]))
179 m.connect("delete_user", "/users/{id}",
179 m.connect("delete_user", "/users/{id}",
180 action="delete", conditions=dict(method=["DELETE"]))
180 action="delete", conditions=dict(method=["DELETE"]))
181 m.connect("edit_user", "/users/{id}/edit",
181 m.connect("edit_user", "/users/{id}/edit",
182 action="edit", conditions=dict(method=["GET"]))
182 action="edit", conditions=dict(method=["GET"]))
183 m.connect("formatted_edit_user",
183 m.connect("formatted_edit_user",
184 "/users/{id}.{format}/edit",
184 "/users/{id}.{format}/edit",
185 action="edit", conditions=dict(method=["GET"]))
185 action="edit", conditions=dict(method=["GET"]))
186 m.connect("user", "/users/{id}",
186 m.connect("user", "/users/{id}",
187 action="show", conditions=dict(method=["GET"]))
187 action="show", conditions=dict(method=["GET"]))
188 m.connect("formatted_user", "/users/{id}.{format}",
188 m.connect("formatted_user", "/users/{id}.{format}",
189 action="show", conditions=dict(method=["GET"]))
189 action="show", conditions=dict(method=["GET"]))
190
190
191 #EXTRAS USER ROUTES
191 #EXTRAS USER ROUTES
192 m.connect("user_perm", "/users_perm/{id}",
192 m.connect("user_perm", "/users_perm/{id}",
193 action="update_perm", conditions=dict(method=["PUT"]))
193 action="update_perm", conditions=dict(method=["PUT"]))
194
194
195 #ADMIN USERS REST ROUTES
195 #ADMIN USERS REST ROUTES
196 with rmap.submapper(path_prefix=ADMIN_PREFIX,
196 with rmap.submapper(path_prefix=ADMIN_PREFIX,
197 controller='admin/users_groups') as m:
197 controller='admin/users_groups') as m:
198 m.connect("users_groups", "/users_groups",
198 m.connect("users_groups", "/users_groups",
199 action="create", conditions=dict(method=["POST"]))
199 action="create", conditions=dict(method=["POST"]))
200 m.connect("users_groups", "/users_groups",
200 m.connect("users_groups", "/users_groups",
201 action="index", conditions=dict(method=["GET"]))
201 action="index", conditions=dict(method=["GET"]))
202 m.connect("formatted_users_groups", "/users_groups.{format}",
202 m.connect("formatted_users_groups", "/users_groups.{format}",
203 action="index", conditions=dict(method=["GET"]))
203 action="index", conditions=dict(method=["GET"]))
204 m.connect("new_users_group", "/users_groups/new",
204 m.connect("new_users_group", "/users_groups/new",
205 action="new", conditions=dict(method=["GET"]))
205 action="new", conditions=dict(method=["GET"]))
206 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
206 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
207 action="new", conditions=dict(method=["GET"]))
207 action="new", conditions=dict(method=["GET"]))
208 m.connect("update_users_group", "/users_groups/{id}",
208 m.connect("update_users_group", "/users_groups/{id}",
209 action="update", conditions=dict(method=["PUT"]))
209 action="update", conditions=dict(method=["PUT"]))
210 m.connect("delete_users_group", "/users_groups/{id}",
210 m.connect("delete_users_group", "/users_groups/{id}",
211 action="delete", conditions=dict(method=["DELETE"]))
211 action="delete", conditions=dict(method=["DELETE"]))
212 m.connect("edit_users_group", "/users_groups/{id}/edit",
212 m.connect("edit_users_group", "/users_groups/{id}/edit",
213 action="edit", conditions=dict(method=["GET"]))
213 action="edit", conditions=dict(method=["GET"]))
214 m.connect("formatted_edit_users_group",
214 m.connect("formatted_edit_users_group",
215 "/users_groups/{id}.{format}/edit",
215 "/users_groups/{id}.{format}/edit",
216 action="edit", conditions=dict(method=["GET"]))
216 action="edit", conditions=dict(method=["GET"]))
217 m.connect("users_group", "/users_groups/{id}",
217 m.connect("users_group", "/users_groups/{id}",
218 action="show", conditions=dict(method=["GET"]))
218 action="show", conditions=dict(method=["GET"]))
219 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
219 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
220 action="show", conditions=dict(method=["GET"]))
220 action="show", conditions=dict(method=["GET"]))
221
221
222 #EXTRAS USER ROUTES
222 #EXTRAS USER ROUTES
223 m.connect("users_group_perm", "/users_groups_perm/{id}",
223 m.connect("users_group_perm", "/users_groups_perm/{id}",
224 action="update_perm", conditions=dict(method=["PUT"]))
224 action="update_perm", conditions=dict(method=["PUT"]))
225
225
226 #ADMIN GROUP REST ROUTES
226 #ADMIN GROUP REST ROUTES
227 rmap.resource('group', 'groups',
227 rmap.resource('group', 'groups',
228 controller='admin/groups', path_prefix=ADMIN_PREFIX)
228 controller='admin/groups', path_prefix=ADMIN_PREFIX)
229
229
230 #ADMIN PERMISSIONS REST ROUTES
230 #ADMIN PERMISSIONS REST ROUTES
231 rmap.resource('permission', 'permissions',
231 rmap.resource('permission', 'permissions',
232 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
232 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
233
233
234 ##ADMIN LDAP SETTINGS
234 ##ADMIN LDAP SETTINGS
235 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
235 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
236 controller='admin/ldap_settings', action='ldap_settings',
236 controller='admin/ldap_settings', action='ldap_settings',
237 conditions=dict(method=["POST"]))
237 conditions=dict(method=["POST"]))
238
238
239 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
239 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
240 controller='admin/ldap_settings')
240 controller='admin/ldap_settings')
241
241
242 #ADMIN SETTINGS REST ROUTES
242 #ADMIN SETTINGS REST ROUTES
243 with rmap.submapper(path_prefix=ADMIN_PREFIX,
243 with rmap.submapper(path_prefix=ADMIN_PREFIX,
244 controller='admin/settings') as m:
244 controller='admin/settings') as m:
245 m.connect("admin_settings", "/settings",
245 m.connect("admin_settings", "/settings",
246 action="create", conditions=dict(method=["POST"]))
246 action="create", conditions=dict(method=["POST"]))
247 m.connect("admin_settings", "/settings",
247 m.connect("admin_settings", "/settings",
248 action="index", conditions=dict(method=["GET"]))
248 action="index", conditions=dict(method=["GET"]))
249 m.connect("formatted_admin_settings", "/settings.{format}",
249 m.connect("formatted_admin_settings", "/settings.{format}",
250 action="index", conditions=dict(method=["GET"]))
250 action="index", conditions=dict(method=["GET"]))
251 m.connect("admin_new_setting", "/settings/new",
251 m.connect("admin_new_setting", "/settings/new",
252 action="new", conditions=dict(method=["GET"]))
252 action="new", conditions=dict(method=["GET"]))
253 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
253 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
254 action="new", conditions=dict(method=["GET"]))
254 action="new", conditions=dict(method=["GET"]))
255 m.connect("/settings/{setting_id}",
255 m.connect("/settings/{setting_id}",
256 action="update", conditions=dict(method=["PUT"]))
256 action="update", conditions=dict(method=["PUT"]))
257 m.connect("/settings/{setting_id}",
257 m.connect("/settings/{setting_id}",
258 action="delete", conditions=dict(method=["DELETE"]))
258 action="delete", conditions=dict(method=["DELETE"]))
259 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
259 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
260 action="edit", conditions=dict(method=["GET"]))
260 action="edit", conditions=dict(method=["GET"]))
261 m.connect("formatted_admin_edit_setting",
261 m.connect("formatted_admin_edit_setting",
262 "/settings/{setting_id}.{format}/edit",
262 "/settings/{setting_id}.{format}/edit",
263 action="edit", conditions=dict(method=["GET"]))
263 action="edit", conditions=dict(method=["GET"]))
264 m.connect("admin_setting", "/settings/{setting_id}",
264 m.connect("admin_setting", "/settings/{setting_id}",
265 action="show", conditions=dict(method=["GET"]))
265 action="show", conditions=dict(method=["GET"]))
266 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
266 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
267 action="show", conditions=dict(method=["GET"]))
267 action="show", conditions=dict(method=["GET"]))
268 m.connect("admin_settings_my_account", "/my_account",
268 m.connect("admin_settings_my_account", "/my_account",
269 action="my_account", conditions=dict(method=["GET"]))
269 action="my_account", conditions=dict(method=["GET"]))
270 m.connect("admin_settings_notifications", "/notifications",
271 action="notifications", conditions=dict(method=["GET"]))
272 m.connect("admin_settings_my_account_update", "/my_account_update",
270 m.connect("admin_settings_my_account_update", "/my_account_update",
273 action="my_account_update", conditions=dict(method=["PUT"]))
271 action="my_account_update", conditions=dict(method=["PUT"]))
274 m.connect("admin_settings_create_repository", "/create_repository",
272 m.connect("admin_settings_create_repository", "/create_repository",
275 action="create_repository", conditions=dict(method=["GET"]))
273 action="create_repository", conditions=dict(method=["GET"]))
276
274
277
275
276 #NOTIFICATION REST ROUTES
277 with rmap.submapper(path_prefix=ADMIN_PREFIX,
278 controller='admin/notifications') as m:
279 m.connect("notifications", "/notifications",
280 action="create", conditions=dict(method=["POST"]))
281 m.connect("notifications", "/notifications",
282 action="index", conditions=dict(method=["GET"]))
283 m.connect("formatted_notifications", "/notifications.{format}",
284 action="index", conditions=dict(method=["GET"]))
285 m.connect("new_notification", "/notifications/new",
286 action="new", conditions=dict(method=["GET"]))
287 m.connect("formatted_new_notification", "/notifications/new.{format}",
288 action="new", conditions=dict(method=["GET"]))
289 m.connect("/notification/{notification_id}",
290 action="update", conditions=dict(method=["PUT"]))
291 m.connect("/notification/{notification_id}",
292 action="delete", conditions=dict(method=["DELETE"]))
293 m.connect("edit_notification", "/notification/{notification_id}/edit",
294 action="edit", conditions=dict(method=["GET"]))
295 m.connect("formatted_edit_notification",
296 "/notification/{notification_id}.{format}/edit",
297 action="edit", conditions=dict(method=["GET"]))
298 m.connect("notification", "/notification/{notification_id}",
299 action="show", conditions=dict(method=["GET"]))
300 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
301 action="show", conditions=dict(method=["GET"]))
302
303
304
278 #ADMIN MAIN PAGES
305 #ADMIN MAIN PAGES
279 with rmap.submapper(path_prefix=ADMIN_PREFIX,
306 with rmap.submapper(path_prefix=ADMIN_PREFIX,
280 controller='admin/admin') as m:
307 controller='admin/admin') as m:
281 m.connect('admin_home', '', action='index')
308 m.connect('admin_home', '', action='index')
282 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
309 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
283 action='add_repo')
310 action='add_repo')
284
311
285 #==========================================================================
312 #==========================================================================
286 # API V1
313 # API V1
287 #==========================================================================
314 #==========================================================================
288 with rmap.submapper(path_prefix=ADMIN_PREFIX,
315 with rmap.submapper(path_prefix=ADMIN_PREFIX,
289 controller='api/api') as m:
316 controller='api/api') as m:
290 m.connect('api', '/api')
317 m.connect('api', '/api')
291
318
292
319
293 #USER JOURNAL
320 #USER JOURNAL
294 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
321 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
295
322
296 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
323 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
297 controller='journal', action="public_journal")
324 controller='journal', action="public_journal")
298
325
299 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
326 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
300 controller='journal', action="public_journal_rss")
327 controller='journal', action="public_journal_rss")
301
328
302 rmap.connect('public_journal_atom',
329 rmap.connect('public_journal_atom',
303 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
330 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
304 action="public_journal_atom")
331 action="public_journal_atom")
305
332
306 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
333 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
307 controller='journal', action='toggle_following',
334 controller='journal', action='toggle_following',
308 conditions=dict(method=["POST"]))
335 conditions=dict(method=["POST"]))
309
336
310 #SEARCH
337 #SEARCH
311 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
338 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
312 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
339 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
313 controller='search')
340 controller='search')
314
341
315 #LOGIN/LOGOUT/REGISTER/SIGN IN
342 #LOGIN/LOGOUT/REGISTER/SIGN IN
316 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
343 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
317 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
344 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
318 action='logout')
345 action='logout')
319
346
320 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
347 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
321 action='register')
348 action='register')
322
349
323 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
350 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
324 controller='login', action='password_reset')
351 controller='login', action='password_reset')
325
352
326 rmap.connect('reset_password_confirmation',
353 rmap.connect('reset_password_confirmation',
327 '%s/password_reset_confirmation' % ADMIN_PREFIX,
354 '%s/password_reset_confirmation' % ADMIN_PREFIX,
328 controller='login', action='password_reset_confirmation')
355 controller='login', action='password_reset_confirmation')
329
356
330 #FEEDS
357 #FEEDS
331 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
358 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
332 controller='feed', action='rss',
359 controller='feed', action='rss',
333 conditions=dict(function=check_repo))
360 conditions=dict(function=check_repo))
334
361
335 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
362 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
336 controller='feed', action='atom',
363 controller='feed', action='atom',
337 conditions=dict(function=check_repo))
364 conditions=dict(function=check_repo))
338
365
339 #==========================================================================
366 #==========================================================================
340 # REPOSITORY ROUTES
367 # REPOSITORY ROUTES
341 #==========================================================================
368 #==========================================================================
342 rmap.connect('summary_home', '/{repo_name:.*}',
369 rmap.connect('summary_home', '/{repo_name:.*}',
343 controller='summary',
370 controller='summary',
344 conditions=dict(function=check_repo))
371 conditions=dict(function=check_repo))
345
372
346 rmap.connect('repos_group_home', '/{group_name:.*}',
373 rmap.connect('repos_group_home', '/{group_name:.*}',
347 controller='admin/repos_groups', action="show_by_name",
374 controller='admin/repos_groups', action="show_by_name",
348 conditions=dict(function=check_group))
375 conditions=dict(function=check_group))
349
376
350 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
377 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
351 controller='changeset', revision='tip',
378 controller='changeset', revision='tip',
352 conditions=dict(function=check_repo))
379 conditions=dict(function=check_repo))
353
380
354 rmap.connect('changeset_comment', '/{repo_name:.*}/changeset/{revision}/comment',
381 rmap.connect('changeset_comment', '/{repo_name:.*}/changeset/{revision}/comment',
355 controller='changeset', revision='tip', action='comment',
382 controller='changeset', revision='tip', action='comment',
356 conditions=dict(function=check_repo))
383 conditions=dict(function=check_repo))
357
384
358 rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
385 rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
359 controller='changeset', action='delete_comment',
386 controller='changeset', action='delete_comment',
360 conditions = dict(function=check_repo, method=["DELETE"]))
387 conditions=dict(function=check_repo, method=["DELETE"]))
361
388
362 rmap.connect('raw_changeset_home',
389 rmap.connect('raw_changeset_home',
363 '/{repo_name:.*}/raw-changeset/{revision}',
390 '/{repo_name:.*}/raw-changeset/{revision}',
364 controller='changeset', action='raw_changeset',
391 controller='changeset', action='raw_changeset',
365 revision='tip', conditions=dict(function=check_repo))
392 revision='tip', conditions=dict(function=check_repo))
366
393
367 rmap.connect('summary_home', '/{repo_name:.*}/summary',
394 rmap.connect('summary_home', '/{repo_name:.*}/summary',
368 controller='summary', conditions=dict(function=check_repo))
395 controller='summary', conditions=dict(function=check_repo))
369
396
370 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
397 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
371 controller='shortlog', conditions=dict(function=check_repo))
398 controller='shortlog', conditions=dict(function=check_repo))
372
399
373 rmap.connect('branches_home', '/{repo_name:.*}/branches',
400 rmap.connect('branches_home', '/{repo_name:.*}/branches',
374 controller='branches', conditions=dict(function=check_repo))
401 controller='branches', conditions=dict(function=check_repo))
375
402
376 rmap.connect('tags_home', '/{repo_name:.*}/tags',
403 rmap.connect('tags_home', '/{repo_name:.*}/tags',
377 controller='tags', conditions=dict(function=check_repo))
404 controller='tags', conditions=dict(function=check_repo))
378
405
379 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
406 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
380 controller='changelog', conditions=dict(function=check_repo))
407 controller='changelog', conditions=dict(function=check_repo))
381
408
382 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
409 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
383 controller='changelog', action='changelog_details',
410 controller='changelog', action='changelog_details',
384 conditions=dict(function=check_repo))
411 conditions=dict(function=check_repo))
385
412
386 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
413 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
387 controller='files', revision='tip', f_path='',
414 controller='files', revision='tip', f_path='',
388 conditions=dict(function=check_repo))
415 conditions=dict(function=check_repo))
389
416
390 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
417 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
391 controller='files', action='diff', revision='tip', f_path='',
418 controller='files', action='diff', revision='tip', f_path='',
392 conditions=dict(function=check_repo))
419 conditions=dict(function=check_repo))
393
420
394 rmap.connect('files_rawfile_home',
421 rmap.connect('files_rawfile_home',
395 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
422 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
396 controller='files', action='rawfile', revision='tip',
423 controller='files', action='rawfile', revision='tip',
397 f_path='', conditions=dict(function=check_repo))
424 f_path='', conditions=dict(function=check_repo))
398
425
399 rmap.connect('files_raw_home',
426 rmap.connect('files_raw_home',
400 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
427 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
401 controller='files', action='raw', revision='tip', f_path='',
428 controller='files', action='raw', revision='tip', f_path='',
402 conditions=dict(function=check_repo))
429 conditions=dict(function=check_repo))
403
430
404 rmap.connect('files_annotate_home',
431 rmap.connect('files_annotate_home',
405 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
432 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
406 controller='files', action='annotate', revision='tip',
433 controller='files', action='annotate', revision='tip',
407 f_path='', conditions=dict(function=check_repo))
434 f_path='', conditions=dict(function=check_repo))
408
435
409 rmap.connect('files_edit_home',
436 rmap.connect('files_edit_home',
410 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
437 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
411 controller='files', action='edit', revision='tip',
438 controller='files', action='edit', revision='tip',
412 f_path='', conditions=dict(function=check_repo))
439 f_path='', conditions=dict(function=check_repo))
413
440
414 rmap.connect('files_add_home',
441 rmap.connect('files_add_home',
415 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
442 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
416 controller='files', action='add', revision='tip',
443 controller='files', action='add', revision='tip',
417 f_path='', conditions=dict(function=check_repo))
444 f_path='', conditions=dict(function=check_repo))
418
445
419 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
446 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
420 controller='files', action='archivefile',
447 controller='files', action='archivefile',
421 conditions=dict(function=check_repo))
448 conditions=dict(function=check_repo))
422
449
423 rmap.connect('files_nodelist_home',
450 rmap.connect('files_nodelist_home',
424 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
451 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
425 controller='files', action='nodelist',
452 controller='files', action='nodelist',
426 conditions=dict(function=check_repo))
453 conditions=dict(function=check_repo))
427
454
428 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
455 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
429 controller='settings', action="delete",
456 controller='settings', action="delete",
430 conditions=dict(method=["DELETE"], function=check_repo))
457 conditions=dict(method=["DELETE"], function=check_repo))
431
458
432 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
459 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
433 controller='settings', action="update",
460 controller='settings', action="update",
434 conditions=dict(method=["PUT"], function=check_repo))
461 conditions=dict(method=["PUT"], function=check_repo))
435
462
436 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
463 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
437 controller='settings', action='index',
464 controller='settings', action='index',
438 conditions=dict(function=check_repo))
465 conditions=dict(function=check_repo))
439
466
440 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
467 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
441 controller='settings', action='fork_create',
468 controller='settings', action='fork_create',
442 conditions=dict(function=check_repo, method=["POST"]))
469 conditions=dict(function=check_repo, method=["POST"]))
443
470
444 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
471 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
445 controller='settings', action='fork',
472 controller='settings', action='fork',
446 conditions=dict(function=check_repo))
473 conditions=dict(function=check_repo))
447
474
448 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
475 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
449 controller='followers', action='followers',
476 controller='followers', action='followers',
450 conditions=dict(function=check_repo))
477 conditions=dict(function=check_repo))
451
478
452 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
479 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
453 controller='forks', action='forks',
480 controller='forks', action='forks',
454 conditions=dict(function=check_repo))
481 conditions=dict(function=check_repo))
455
482
456 return rmap
483 return rmap
@@ -1,416 +1,408 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from sqlalchemy import func
30 from sqlalchemy import func
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, session, tmpl_context as c, url, config
32 from pylons import request, session, tmpl_context as c, url, config
33 from pylons.controllers.util import abort, redirect
33 from pylons.controllers.util import abort, redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 HasPermissionAnyDecorator, NotAnonymous
38 HasPermissionAnyDecorator, NotAnonymous
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.celerylib import tasks, run_task
40 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
42 set_rhodecode_config, repo_name_slug
42 set_rhodecode_config, repo_name_slug
43 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
43 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
44 RhodeCodeSetting
44 RhodeCodeSetting
45 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
45 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
46 ApplicationUiSettingsForm
46 ApplicationUiSettingsForm
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49 from rhodecode.model.db import User
49 from rhodecode.model.db import User
50 from rhodecode.model.notification import NotificationModel
50 from rhodecode.model.notification import NotificationModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class SettingsController(BaseController):
55 class SettingsController(BaseController):
56 """REST Controller styled on the Atom Publishing Protocol"""
56 """REST Controller styled on the Atom Publishing Protocol"""
57 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
58 # file has a resource setup:
58 # file has a resource setup:
59 # map.resource('setting', 'settings', controller='admin/settings',
59 # map.resource('setting', 'settings', controller='admin/settings',
60 # path_prefix='/admin', name_prefix='admin_')
60 # path_prefix='/admin', name_prefix='admin_')
61
61
62 @LoginRequired()
62 @LoginRequired()
63 def __before__(self):
63 def __before__(self):
64 c.admin_user = session.get('admin_user')
64 c.admin_user = session.get('admin_user')
65 c.admin_username = session.get('admin_username')
65 c.admin_username = session.get('admin_username')
66 super(SettingsController, self).__before__()
66 super(SettingsController, self).__before__()
67
67
68 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
69 def index(self, format='html'):
69 def index(self, format='html'):
70 """GET /admin/settings: All items in the collection"""
70 """GET /admin/settings: All items in the collection"""
71 # url('admin_settings')
71 # url('admin_settings')
72
72
73 defaults = RhodeCodeSetting.get_app_settings()
73 defaults = RhodeCodeSetting.get_app_settings()
74 defaults.update(self.get_hg_ui_settings())
74 defaults.update(self.get_hg_ui_settings())
75 return htmlfill.render(
75 return htmlfill.render(
76 render('admin/settings/settings.html'),
76 render('admin/settings/settings.html'),
77 defaults=defaults,
77 defaults=defaults,
78 encoding="UTF-8",
78 encoding="UTF-8",
79 force_defaults=False
79 force_defaults=False
80 )
80 )
81
81
82 @HasPermissionAllDecorator('hg.admin')
82 @HasPermissionAllDecorator('hg.admin')
83 def create(self):
83 def create(self):
84 """POST /admin/settings: Create a new item"""
84 """POST /admin/settings: Create a new item"""
85 # url('admin_settings')
85 # url('admin_settings')
86
86
87 @HasPermissionAllDecorator('hg.admin')
87 @HasPermissionAllDecorator('hg.admin')
88 def new(self, format='html'):
88 def new(self, format='html'):
89 """GET /admin/settings/new: Form to create a new item"""
89 """GET /admin/settings/new: Form to create a new item"""
90 # url('admin_new_setting')
90 # url('admin_new_setting')
91
91
92 @HasPermissionAllDecorator('hg.admin')
92 @HasPermissionAllDecorator('hg.admin')
93 def update(self, setting_id):
93 def update(self, setting_id):
94 """PUT /admin/settings/setting_id: Update an existing item"""
94 """PUT /admin/settings/setting_id: Update an existing item"""
95 # Forms posted to this method should contain a hidden field:
95 # Forms posted to this method should contain a hidden field:
96 # <input type="hidden" name="_method" value="PUT" />
96 # <input type="hidden" name="_method" value="PUT" />
97 # Or using helpers:
97 # Or using helpers:
98 # h.form(url('admin_setting', setting_id=ID),
98 # h.form(url('admin_setting', setting_id=ID),
99 # method='put')
99 # method='put')
100 # url('admin_setting', setting_id=ID)
100 # url('admin_setting', setting_id=ID)
101 if setting_id == 'mapping':
101 if setting_id == 'mapping':
102 rm_obsolete = request.POST.get('destroy', False)
102 rm_obsolete = request.POST.get('destroy', False)
103 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
103 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
104 initial = ScmModel().repo_scan()
104 initial = ScmModel().repo_scan()
105 log.debug('invalidating all repositories')
105 log.debug('invalidating all repositories')
106 for repo_name in initial.keys():
106 for repo_name in initial.keys():
107 invalidate_cache('get_repo_cached_%s' % repo_name)
107 invalidate_cache('get_repo_cached_%s' % repo_name)
108
108
109 added, removed = repo2db_mapper(initial, rm_obsolete)
109 added, removed = repo2db_mapper(initial, rm_obsolete)
110
110
111 h.flash(_('Repositories successfully'
111 h.flash(_('Repositories successfully'
112 ' rescanned added: %s,removed: %s') % (added, removed),
112 ' rescanned added: %s,removed: %s') % (added, removed),
113 category='success')
113 category='success')
114
114
115 if setting_id == 'whoosh':
115 if setting_id == 'whoosh':
116 repo_location = self.get_hg_ui_settings()['paths_root_path']
116 repo_location = self.get_hg_ui_settings()['paths_root_path']
117 full_index = request.POST.get('full_index', False)
117 full_index = request.POST.get('full_index', False)
118 run_task(tasks.whoosh_index, repo_location, full_index)
118 run_task(tasks.whoosh_index, repo_location, full_index)
119
119
120 h.flash(_('Whoosh reindex task scheduled'), category='success')
120 h.flash(_('Whoosh reindex task scheduled'), category='success')
121 if setting_id == 'global':
121 if setting_id == 'global':
122
122
123 application_form = ApplicationSettingsForm()()
123 application_form = ApplicationSettingsForm()()
124 try:
124 try:
125 form_result = application_form.to_python(dict(request.POST))
125 form_result = application_form.to_python(dict(request.POST))
126
126
127 try:
127 try:
128 hgsettings1 = RhodeCodeSetting.get_by_name('title')
128 hgsettings1 = RhodeCodeSetting.get_by_name('title')
129 hgsettings1.app_settings_value = \
129 hgsettings1.app_settings_value = \
130 form_result['rhodecode_title']
130 form_result['rhodecode_title']
131
131
132 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
132 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
133 hgsettings2.app_settings_value = \
133 hgsettings2.app_settings_value = \
134 form_result['rhodecode_realm']
134 form_result['rhodecode_realm']
135
135
136 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
136 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
137 hgsettings3.app_settings_value = \
137 hgsettings3.app_settings_value = \
138 form_result['rhodecode_ga_code']
138 form_result['rhodecode_ga_code']
139
139
140 self.sa.add(hgsettings1)
140 self.sa.add(hgsettings1)
141 self.sa.add(hgsettings2)
141 self.sa.add(hgsettings2)
142 self.sa.add(hgsettings3)
142 self.sa.add(hgsettings3)
143 self.sa.commit()
143 self.sa.commit()
144 set_rhodecode_config(config)
144 set_rhodecode_config(config)
145 h.flash(_('Updated application settings'),
145 h.flash(_('Updated application settings'),
146 category='success')
146 category='success')
147
147
148 except Exception:
148 except Exception:
149 log.error(traceback.format_exc())
149 log.error(traceback.format_exc())
150 h.flash(_('error occurred during updating '
150 h.flash(_('error occurred during updating '
151 'application settings'),
151 'application settings'),
152 category='error')
152 category='error')
153
153
154 self.sa.rollback()
154 self.sa.rollback()
155
155
156 except formencode.Invalid, errors:
156 except formencode.Invalid, errors:
157 return htmlfill.render(
157 return htmlfill.render(
158 render('admin/settings/settings.html'),
158 render('admin/settings/settings.html'),
159 defaults=errors.value,
159 defaults=errors.value,
160 errors=errors.error_dict or {},
160 errors=errors.error_dict or {},
161 prefix_error=False,
161 prefix_error=False,
162 encoding="UTF-8")
162 encoding="UTF-8")
163
163
164 if setting_id == 'mercurial':
164 if setting_id == 'mercurial':
165 application_form = ApplicationUiSettingsForm()()
165 application_form = ApplicationUiSettingsForm()()
166 try:
166 try:
167 form_result = application_form.to_python(dict(request.POST))
167 form_result = application_form.to_python(dict(request.POST))
168
168
169 try:
169 try:
170
170
171 hgsettings1 = self.sa.query(RhodeCodeUi)\
171 hgsettings1 = self.sa.query(RhodeCodeUi)\
172 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
172 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
173 hgsettings1.ui_value = form_result['web_push_ssl']
173 hgsettings1.ui_value = form_result['web_push_ssl']
174
174
175 hgsettings2 = self.sa.query(RhodeCodeUi)\
175 hgsettings2 = self.sa.query(RhodeCodeUi)\
176 .filter(RhodeCodeUi.ui_key == '/').one()
176 .filter(RhodeCodeUi.ui_key == '/').one()
177 hgsettings2.ui_value = form_result['paths_root_path']
177 hgsettings2.ui_value = form_result['paths_root_path']
178
178
179 #HOOKS
179 #HOOKS
180 hgsettings3 = self.sa.query(RhodeCodeUi)\
180 hgsettings3 = self.sa.query(RhodeCodeUi)\
181 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
181 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
182 hgsettings3.ui_active = \
182 hgsettings3.ui_active = \
183 bool(form_result['hooks_changegroup_update'])
183 bool(form_result['hooks_changegroup_update'])
184
184
185 hgsettings4 = self.sa.query(RhodeCodeUi)\
185 hgsettings4 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key ==
186 .filter(RhodeCodeUi.ui_key ==
187 'changegroup.repo_size').one()
187 'changegroup.repo_size').one()
188 hgsettings4.ui_active = \
188 hgsettings4.ui_active = \
189 bool(form_result['hooks_changegroup_repo_size'])
189 bool(form_result['hooks_changegroup_repo_size'])
190
190
191 hgsettings5 = self.sa.query(RhodeCodeUi)\
191 hgsettings5 = self.sa.query(RhodeCodeUi)\
192 .filter(RhodeCodeUi.ui_key ==
192 .filter(RhodeCodeUi.ui_key ==
193 'pretxnchangegroup.push_logger').one()
193 'pretxnchangegroup.push_logger').one()
194 hgsettings5.ui_active = \
194 hgsettings5.ui_active = \
195 bool(form_result['hooks_pretxnchangegroup'
195 bool(form_result['hooks_pretxnchangegroup'
196 '_push_logger'])
196 '_push_logger'])
197
197
198 hgsettings6 = self.sa.query(RhodeCodeUi)\
198 hgsettings6 = self.sa.query(RhodeCodeUi)\
199 .filter(RhodeCodeUi.ui_key ==
199 .filter(RhodeCodeUi.ui_key ==
200 'preoutgoing.pull_logger').one()
200 'preoutgoing.pull_logger').one()
201 hgsettings6.ui_active = \
201 hgsettings6.ui_active = \
202 bool(form_result['hooks_preoutgoing_pull_logger'])
202 bool(form_result['hooks_preoutgoing_pull_logger'])
203
203
204 self.sa.add(hgsettings1)
204 self.sa.add(hgsettings1)
205 self.sa.add(hgsettings2)
205 self.sa.add(hgsettings2)
206 self.sa.add(hgsettings3)
206 self.sa.add(hgsettings3)
207 self.sa.add(hgsettings4)
207 self.sa.add(hgsettings4)
208 self.sa.add(hgsettings5)
208 self.sa.add(hgsettings5)
209 self.sa.add(hgsettings6)
209 self.sa.add(hgsettings6)
210 self.sa.commit()
210 self.sa.commit()
211
211
212 h.flash(_('Updated mercurial settings'),
212 h.flash(_('Updated mercurial settings'),
213 category='success')
213 category='success')
214
214
215 except:
215 except:
216 log.error(traceback.format_exc())
216 log.error(traceback.format_exc())
217 h.flash(_('error occurred during updating '
217 h.flash(_('error occurred during updating '
218 'application settings'), category='error')
218 'application settings'), category='error')
219
219
220 self.sa.rollback()
220 self.sa.rollback()
221
221
222 except formencode.Invalid, errors:
222 except formencode.Invalid, errors:
223 return htmlfill.render(
223 return htmlfill.render(
224 render('admin/settings/settings.html'),
224 render('admin/settings/settings.html'),
225 defaults=errors.value,
225 defaults=errors.value,
226 errors=errors.error_dict or {},
226 errors=errors.error_dict or {},
227 prefix_error=False,
227 prefix_error=False,
228 encoding="UTF-8")
228 encoding="UTF-8")
229
229
230
230
231 if setting_id == 'hooks':
231 if setting_id == 'hooks':
232 ui_key = request.POST.get('new_hook_ui_key')
232 ui_key = request.POST.get('new_hook_ui_key')
233 ui_value = request.POST.get('new_hook_ui_value')
233 ui_value = request.POST.get('new_hook_ui_value')
234 try:
234 try:
235
235
236 if ui_value and ui_key:
236 if ui_value and ui_key:
237 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
237 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
238 h.flash(_('Added new hook'),
238 h.flash(_('Added new hook'),
239 category='success')
239 category='success')
240
240
241 # check for edits
241 # check for edits
242 update = False
242 update = False
243 _d = request.POST.dict_of_lists()
243 _d = request.POST.dict_of_lists()
244 for k, v in zip(_d.get('hook_ui_key', []), _d.get('hook_ui_value_new', [])):
244 for k, v in zip(_d.get('hook_ui_key', []), _d.get('hook_ui_value_new', [])):
245 RhodeCodeUi.create_or_update_hook(k, v)
245 RhodeCodeUi.create_or_update_hook(k, v)
246 update = True
246 update = True
247
247
248 if update:
248 if update:
249 h.flash(_('Updated hooks'), category='success')
249 h.flash(_('Updated hooks'), category='success')
250
250
251 except:
251 except:
252 log.error(traceback.format_exc())
252 log.error(traceback.format_exc())
253 h.flash(_('error occurred during hook creation'),
253 h.flash(_('error occurred during hook creation'),
254 category='error')
254 category='error')
255
255
256 return redirect(url('admin_edit_setting', setting_id='hooks'))
256 return redirect(url('admin_edit_setting', setting_id='hooks'))
257
257
258
258
259
259
260 if setting_id == 'email':
260 if setting_id == 'email':
261 test_email = request.POST.get('test_email')
261 test_email = request.POST.get('test_email')
262 test_email_subj = 'RhodeCode TestEmail'
262 test_email_subj = 'RhodeCode TestEmail'
263 test_email_body = 'RhodeCode Email test'
263 test_email_body = 'RhodeCode Email test'
264
264
265 run_task(tasks.send_email, [test_email], test_email_subj,
265 run_task(tasks.send_email, [test_email], test_email_subj,
266 test_email_body)
266 test_email_body)
267 h.flash(_('Email task created'), category='success')
267 h.flash(_('Email task created'), category='success')
268 return redirect(url('admin_settings'))
268 return redirect(url('admin_settings'))
269
269
270 @HasPermissionAllDecorator('hg.admin')
270 @HasPermissionAllDecorator('hg.admin')
271 def delete(self, setting_id):
271 def delete(self, setting_id):
272 """DELETE /admin/settings/setting_id: Delete an existing item"""
272 """DELETE /admin/settings/setting_id: Delete an existing item"""
273 # Forms posted to this method should contain a hidden field:
273 # Forms posted to this method should contain a hidden field:
274 # <input type="hidden" name="_method" value="DELETE" />
274 # <input type="hidden" name="_method" value="DELETE" />
275 # Or using helpers:
275 # Or using helpers:
276 # h.form(url('admin_setting', setting_id=ID),
276 # h.form(url('admin_setting', setting_id=ID),
277 # method='delete')
277 # method='delete')
278 # url('admin_setting', setting_id=ID)
278 # url('admin_setting', setting_id=ID)
279 if setting_id == 'hooks':
279 if setting_id == 'hooks':
280 hook_id = request.POST.get('hook_id')
280 hook_id = request.POST.get('hook_id')
281 RhodeCodeUi.delete(hook_id)
281 RhodeCodeUi.delete(hook_id)
282
282
283
283
284 @HasPermissionAllDecorator('hg.admin')
284 @HasPermissionAllDecorator('hg.admin')
285 def show(self, setting_id, format='html'):
285 def show(self, setting_id, format='html'):
286 """
286 """
287 GET /admin/settings/setting_id: Show a specific item"""
287 GET /admin/settings/setting_id: Show a specific item"""
288 # url('admin_setting', setting_id=ID)
288 # url('admin_setting', setting_id=ID)
289
289
290 @HasPermissionAllDecorator('hg.admin')
290 @HasPermissionAllDecorator('hg.admin')
291 def edit(self, setting_id, format='html'):
291 def edit(self, setting_id, format='html'):
292 """
292 """
293 GET /admin/settings/setting_id/edit: Form to
293 GET /admin/settings/setting_id/edit: Form to
294 edit an existing item"""
294 edit an existing item"""
295 # url('admin_edit_setting', setting_id=ID)
295 # url('admin_edit_setting', setting_id=ID)
296
296
297 c.hooks = RhodeCodeUi.get_builtin_hooks()
297 c.hooks = RhodeCodeUi.get_builtin_hooks()
298 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
298 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
299
299
300 return htmlfill.render(
300 return htmlfill.render(
301 render('admin/settings/hooks.html'),
301 render('admin/settings/hooks.html'),
302 defaults={},
302 defaults={},
303 encoding="UTF-8",
303 encoding="UTF-8",
304 force_defaults=False
304 force_defaults=False
305 )
305 )
306
306
307 @NotAnonymous()
307 @NotAnonymous()
308 def my_account(self):
308 def my_account(self):
309 """
309 """
310 GET /_admin/my_account Displays info about my account
310 GET /_admin/my_account Displays info about my account
311 """
311 """
312 # url('admin_settings_my_account')
312 # url('admin_settings_my_account')
313
313
314 c.user = User.get(self.rhodecode_user.user_id)
314 c.user = User.get(self.rhodecode_user.user_id)
315 all_repos = self.sa.query(Repository)\
315 all_repos = self.sa.query(Repository)\
316 .filter(Repository.user_id == c.user.user_id)\
316 .filter(Repository.user_id == c.user.user_id)\
317 .order_by(func.lower(Repository.repo_name)).all()
317 .order_by(func.lower(Repository.repo_name)).all()
318
318
319 c.user_repos = ScmModel().get_repos(all_repos)
319 c.user_repos = ScmModel().get_repos(all_repos)
320
320
321 if c.user.username == 'default':
321 if c.user.username == 'default':
322 h.flash(_("You can't edit this user since it's"
322 h.flash(_("You can't edit this user since it's"
323 " crucial for entire application"), category='warning')
323 " crucial for entire application"), category='warning')
324 return redirect(url('users'))
324 return redirect(url('users'))
325
325
326 defaults = c.user.get_dict()
326 defaults = c.user.get_dict()
327 return htmlfill.render(
327 return htmlfill.render(
328 render('admin/users/user_edit_my_account.html'),
328 render('admin/users/user_edit_my_account.html'),
329 defaults=defaults,
329 defaults=defaults,
330 encoding="UTF-8",
330 encoding="UTF-8",
331 force_defaults=False
331 force_defaults=False
332 )
332 )
333
333
334 def my_account_update(self):
334 def my_account_update(self):
335 """PUT /_admin/my_account_update: Update an existing item"""
335 """PUT /_admin/my_account_update: Update an existing item"""
336 # Forms posted to this method should contain a hidden field:
336 # Forms posted to this method should contain a hidden field:
337 # <input type="hidden" name="_method" value="PUT" />
337 # <input type="hidden" name="_method" value="PUT" />
338 # Or using helpers:
338 # Or using helpers:
339 # h.form(url('admin_settings_my_account_update'),
339 # h.form(url('admin_settings_my_account_update'),
340 # method='put')
340 # method='put')
341 # url('admin_settings_my_account_update', id=ID)
341 # url('admin_settings_my_account_update', id=ID)
342 user_model = UserModel()
342 user_model = UserModel()
343 uid = self.rhodecode_user.user_id
343 uid = self.rhodecode_user.user_id
344 _form = UserForm(edit=True,
344 _form = UserForm(edit=True,
345 old_data={'user_id': uid,
345 old_data={'user_id': uid,
346 'email': self.rhodecode_user.email})()
346 'email': self.rhodecode_user.email})()
347 form_result = {}
347 form_result = {}
348 try:
348 try:
349 form_result = _form.to_python(dict(request.POST))
349 form_result = _form.to_python(dict(request.POST))
350 user_model.update_my_account(uid, form_result)
350 user_model.update_my_account(uid, form_result)
351 h.flash(_('Your account was updated successfully'),
351 h.flash(_('Your account was updated successfully'),
352 category='success')
352 category='success')
353
353
354 except formencode.Invalid, errors:
354 except formencode.Invalid, errors:
355 c.user = User.get(self.rhodecode_user.user_id)
355 c.user = User.get(self.rhodecode_user.user_id)
356 all_repos = self.sa.query(Repository)\
356 all_repos = self.sa.query(Repository)\
357 .filter(Repository.user_id == c.user.user_id)\
357 .filter(Repository.user_id == c.user.user_id)\
358 .order_by(func.lower(Repository.repo_name))\
358 .order_by(func.lower(Repository.repo_name))\
359 .all()
359 .all()
360 c.user_repos = ScmModel().get_repos(all_repos)
360 c.user_repos = ScmModel().get_repos(all_repos)
361
361
362 return htmlfill.render(
362 return htmlfill.render(
363 render('admin/users/user_edit_my_account.html'),
363 render('admin/users/user_edit_my_account.html'),
364 defaults=errors.value,
364 defaults=errors.value,
365 errors=errors.error_dict or {},
365 errors=errors.error_dict or {},
366 prefix_error=False,
366 prefix_error=False,
367 encoding="UTF-8")
367 encoding="UTF-8")
368 except Exception:
368 except Exception:
369 log.error(traceback.format_exc())
369 log.error(traceback.format_exc())
370 h.flash(_('error occurred during update of user %s') \
370 h.flash(_('error occurred during update of user %s') \
371 % form_result.get('username'), category='error')
371 % form_result.get('username'), category='error')
372
372
373 return redirect(url('my_account'))
373 return redirect(url('my_account'))
374
374
375
376 @NotAnonymous()
377 def notifications(self):
378 c.user = User.get(self.rhodecode_user.user_id)
379 c.notifications = NotificationModel().get_for_user(c.user.user_id)
380 return render('admin/users/notifications.html'),
381
382
383 @NotAnonymous()
375 @NotAnonymous()
384 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
376 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
385 def create_repository(self):
377 def create_repository(self):
386 """GET /_admin/create_repository: Form to create a new item"""
378 """GET /_admin/create_repository: Form to create a new item"""
387
379
388 c.repo_groups = RepoGroup.groups_choices()
380 c.repo_groups = RepoGroup.groups_choices()
389 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
381 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
390
382
391 new_repo = request.GET.get('repo', '')
383 new_repo = request.GET.get('repo', '')
392 c.new_repo = repo_name_slug(new_repo)
384 c.new_repo = repo_name_slug(new_repo)
393
385
394 return render('admin/repos/repo_add_create_repository.html')
386 return render('admin/repos/repo_add_create_repository.html')
395
387
396 def get_hg_ui_settings(self):
388 def get_hg_ui_settings(self):
397 ret = self.sa.query(RhodeCodeUi).all()
389 ret = self.sa.query(RhodeCodeUi).all()
398
390
399 if not ret:
391 if not ret:
400 raise Exception('Could not get application ui settings !')
392 raise Exception('Could not get application ui settings !')
401 settings = {}
393 settings = {}
402 for each in ret:
394 for each in ret:
403 k = each.ui_key
395 k = each.ui_key
404 v = each.ui_value
396 v = each.ui_value
405 if k == '/':
397 if k == '/':
406 k = 'root_path'
398 k = 'root_path'
407
399
408 if k.find('.') != -1:
400 if k.find('.') != -1:
409 k = k.replace('.', '_')
401 k = k.replace('.', '_')
410
402
411 if each.ui_section == 'hooks':
403 if each.ui_section == 'hooks':
412 v = each.ui_active
404 v = each.ui_active
413
405
414 settings[each.ui_section + '_' + k] = v
406 settings[each.ui_section + '_' + k] = v
415
407
416 return settings
408 return settings
@@ -1,298 +1,296 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons showoing changes beetween
6 changeset controller for pylons showoing changes beetween
7 revisions
7 revisions
8
8
9 :created_on: Apr 25, 2010
9 :created_on: Apr 25, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons import tmpl_context as c, url, request, response
29 from pylons import tmpl_context as c, url, request, response
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.decorators import jsonify
32 from pylons.decorators import jsonify
33
33
34 import rhodecode.lib.helpers as h
34 import rhodecode.lib.helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 NotAnonymous
37 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.base import BaseRepoController, render
38 from rhodecode.lib.utils import EmptyChangeset
37 from rhodecode.lib.utils import EmptyChangeset
39 from rhodecode.lib.compat import OrderedDict
38 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.model.db import ChangesetComment
39 from rhodecode.model.db import ChangesetComment
41 from rhodecode.model.comment import ChangesetCommentsModel
40 from rhodecode.model.comment import ChangesetCommentsModel
42
41
43 from vcs.exceptions import RepositoryError, ChangesetError, \
42 from vcs.exceptions import RepositoryError, ChangesetError, \
44 ChangesetDoesNotExistError
43 ChangesetDoesNotExistError
45 from vcs.nodes import FileNode
44 from vcs.nodes import FileNode
46 from vcs.utils import diffs as differ
45 from vcs.utils import diffs as differ
47 from webob.exc import HTTPForbidden
46 from webob.exc import HTTPForbidden
48
47
49 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
50
49
51
50
52 class ChangesetController(BaseRepoController):
51 class ChangesetController(BaseRepoController):
53
52
54 @LoginRequired()
53 @LoginRequired()
55 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
54 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
56 'repository.admin')
55 'repository.admin')
57 def __before__(self):
56 def __before__(self):
58 super(ChangesetController, self).__before__()
57 super(ChangesetController, self).__before__()
59 c.affected_files_cut_off = 60
58 c.affected_files_cut_off = 60
60
59
61 def index(self, revision):
60 def index(self, revision):
62
61
63 def wrap_to_table(str):
62 def wrap_to_table(str):
64
63
65 return '''<table class="code-difftable">
64 return '''<table class="code-difftable">
66 <tr class="line">
65 <tr class="line">
67 <td class="lineno new"></td>
66 <td class="lineno new"></td>
68 <td class="code"><pre>%s</pre></td>
67 <td class="code"><pre>%s</pre></td>
69 </tr>
68 </tr>
70 </table>''' % str
69 </table>''' % str
71
70
72 #get ranges of revisions if preset
71 #get ranges of revisions if preset
73 rev_range = revision.split('...')[:2]
72 rev_range = revision.split('...')[:2]
74
73
75 try:
74 try:
76 if len(rev_range) == 2:
75 if len(rev_range) == 2:
77 rev_start = rev_range[0]
76 rev_start = rev_range[0]
78 rev_end = rev_range[1]
77 rev_end = rev_range[1]
79 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
78 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
80 end=rev_end)
79 end=rev_end)
81 else:
80 else:
82 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
81 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
83
82
84 c.cs_ranges = list(rev_ranges)
83 c.cs_ranges = list(rev_ranges)
85 if not c.cs_ranges:
84 if not c.cs_ranges:
86 raise RepositoryError('Changeset range returned empty result')
85 raise RepositoryError('Changeset range returned empty result')
87
86
88 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
87 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
89 log.error(traceback.format_exc())
88 log.error(traceback.format_exc())
90 h.flash(str(e), category='warning')
89 h.flash(str(e), category='warning')
91 return redirect(url('home'))
90 return redirect(url('home'))
92
91
93 c.changes = OrderedDict()
92 c.changes = OrderedDict()
94 c.sum_added = 0
93 c.sum_added = 0
95 c.sum_removed = 0
94 c.sum_removed = 0
96 c.lines_added = 0
95 c.lines_added = 0
97 c.lines_deleted = 0
96 c.lines_deleted = 0
98 c.cut_off = False # defines if cut off limit is reached
97 c.cut_off = False # defines if cut off limit is reached
99
98
100 c.comments = []
99 c.comments = []
101 c.inline_comments = []
100 c.inline_comments = []
102 c.inline_cnt = 0
101 c.inline_cnt = 0
103 # Iterate over ranges (default changeset view is always one changeset)
102 # Iterate over ranges (default changeset view is always one changeset)
104 for changeset in c.cs_ranges:
103 for changeset in c.cs_ranges:
105 c.comments.extend(ChangesetCommentsModel()\
104 c.comments.extend(ChangesetCommentsModel()\
106 .get_comments(c.rhodecode_db_repo.repo_id,
105 .get_comments(c.rhodecode_db_repo.repo_id,
107 changeset.raw_id))
106 changeset.raw_id))
108 inlines = ChangesetCommentsModel()\
107 inlines = ChangesetCommentsModel()\
109 .get_inline_comments(c.rhodecode_db_repo.repo_id,
108 .get_inline_comments(c.rhodecode_db_repo.repo_id,
110 changeset.raw_id)
109 changeset.raw_id)
111 c.inline_comments.extend(inlines)
110 c.inline_comments.extend(inlines)
112 c.changes[changeset.raw_id] = []
111 c.changes[changeset.raw_id] = []
113 try:
112 try:
114 changeset_parent = changeset.parents[0]
113 changeset_parent = changeset.parents[0]
115 except IndexError:
114 except IndexError:
116 changeset_parent = None
115 changeset_parent = None
117
116
118 #==================================================================
117 #==================================================================
119 # ADDED FILES
118 # ADDED FILES
120 #==================================================================
119 #==================================================================
121 for node in changeset.added:
120 for node in changeset.added:
122
121
123 filenode_old = FileNode(node.path, '', EmptyChangeset())
122 filenode_old = FileNode(node.path, '', EmptyChangeset())
124 if filenode_old.is_binary or node.is_binary:
123 if filenode_old.is_binary or node.is_binary:
125 diff = wrap_to_table(_('binary file'))
124 diff = wrap_to_table(_('binary file'))
126 st = (0, 0)
125 st = (0, 0)
127 else:
126 else:
128 # in this case node.size is good parameter since those are
127 # in this case node.size is good parameter since those are
129 # added nodes and their size defines how many changes were
128 # added nodes and their size defines how many changes were
130 # made
129 # made
131 c.sum_added += node.size
130 c.sum_added += node.size
132 if c.sum_added < self.cut_off_limit:
131 if c.sum_added < self.cut_off_limit:
133 f_gitdiff = differ.get_gitdiff(filenode_old, node)
132 f_gitdiff = differ.get_gitdiff(filenode_old, node)
134 d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
133 d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
135
134
136 st = d.stat()
135 st = d.stat()
137 diff = d.as_html()
136 diff = d.as_html()
138
137
139 else:
138 else:
140 diff = wrap_to_table(_('Changeset is to big and '
139 diff = wrap_to_table(_('Changeset is to big and '
141 'was cut off, see raw '
140 'was cut off, see raw '
142 'changeset instead'))
141 'changeset instead'))
143 c.cut_off = True
142 c.cut_off = True
144 break
143 break
145
144
146 cs1 = None
145 cs1 = None
147 cs2 = node.last_changeset.raw_id
146 cs2 = node.last_changeset.raw_id
148 c.lines_added += st[0]
147 c.lines_added += st[0]
149 c.lines_deleted += st[1]
148 c.lines_deleted += st[1]
150 c.changes[changeset.raw_id].append(('added', node, diff,
149 c.changes[changeset.raw_id].append(('added', node, diff,
151 cs1, cs2, st))
150 cs1, cs2, st))
152
151
153 #==================================================================
152 #==================================================================
154 # CHANGED FILES
153 # CHANGED FILES
155 #==================================================================
154 #==================================================================
156 if not c.cut_off:
155 if not c.cut_off:
157 for node in changeset.changed:
156 for node in changeset.changed:
158 try:
157 try:
159 filenode_old = changeset_parent.get_node(node.path)
158 filenode_old = changeset_parent.get_node(node.path)
160 except ChangesetError:
159 except ChangesetError:
161 log.warning('Unable to fetch parent node for diff')
160 log.warning('Unable to fetch parent node for diff')
162 filenode_old = FileNode(node.path, '',
161 filenode_old = FileNode(node.path, '',
163 EmptyChangeset())
162 EmptyChangeset())
164
163
165 if filenode_old.is_binary or node.is_binary:
164 if filenode_old.is_binary or node.is_binary:
166 diff = wrap_to_table(_('binary file'))
165 diff = wrap_to_table(_('binary file'))
167 st = (0, 0)
166 st = (0, 0)
168 else:
167 else:
169
168
170 if c.sum_removed < self.cut_off_limit:
169 if c.sum_removed < self.cut_off_limit:
171 f_gitdiff = differ.get_gitdiff(filenode_old, node)
170 f_gitdiff = differ.get_gitdiff(filenode_old, node)
172 d = differ.DiffProcessor(f_gitdiff,
171 d = differ.DiffProcessor(f_gitdiff,
173 format='gitdiff')
172 format='gitdiff')
174 st = d.stat()
173 st = d.stat()
175 if (st[0] + st[1]) * 256 > self.cut_off_limit:
174 if (st[0] + st[1]) * 256 > self.cut_off_limit:
176 diff = wrap_to_table(_('Diff is to big '
175 diff = wrap_to_table(_('Diff is to big '
177 'and was cut off, see '
176 'and was cut off, see '
178 'raw diff instead'))
177 'raw diff instead'))
179 else:
178 else:
180 diff = d.as_html()
179 diff = d.as_html()
181
180
182 if diff:
181 if diff:
183 c.sum_removed += len(diff)
182 c.sum_removed += len(diff)
184 else:
183 else:
185 diff = wrap_to_table(_('Changeset is to big and '
184 diff = wrap_to_table(_('Changeset is to big and '
186 'was cut off, see raw '
185 'was cut off, see raw '
187 'changeset instead'))
186 'changeset instead'))
188 c.cut_off = True
187 c.cut_off = True
189 break
188 break
190
189
191 cs1 = filenode_old.last_changeset.raw_id
190 cs1 = filenode_old.last_changeset.raw_id
192 cs2 = node.last_changeset.raw_id
191 cs2 = node.last_changeset.raw_id
193 c.lines_added += st[0]
192 c.lines_added += st[0]
194 c.lines_deleted += st[1]
193 c.lines_deleted += st[1]
195 c.changes[changeset.raw_id].append(('changed', node, diff,
194 c.changes[changeset.raw_id].append(('changed', node, diff,
196 cs1, cs2, st))
195 cs1, cs2, st))
197
196
198 #==================================================================
197 #==================================================================
199 # REMOVED FILES
198 # REMOVED FILES
200 #==================================================================
199 #==================================================================
201 if not c.cut_off:
200 if not c.cut_off:
202 for node in changeset.removed:
201 for node in changeset.removed:
203 c.changes[changeset.raw_id].append(('removed', node, None,
202 c.changes[changeset.raw_id].append(('removed', node, None,
204 None, None, (0, 0)))
203 None, None, (0, 0)))
205
204
206 # count inline comments
205 # count inline comments
207 for path, lines in c.inline_comments:
206 for path, lines in c.inline_comments:
208 for comments in lines.values():
207 for comments in lines.values():
209 c.inline_cnt += len(comments)
208 c.inline_cnt += len(comments)
210
209
211 if len(c.cs_ranges) == 1:
210 if len(c.cs_ranges) == 1:
212 c.changeset = c.cs_ranges[0]
211 c.changeset = c.cs_ranges[0]
213 c.changes = c.changes[c.changeset.raw_id]
212 c.changes = c.changes[c.changeset.raw_id]
214
213
215 return render('changeset/changeset.html')
214 return render('changeset/changeset.html')
216 else:
215 else:
217 return render('changeset/changeset_range.html')
216 return render('changeset/changeset_range.html')
218
217
219 def raw_changeset(self, revision):
218 def raw_changeset(self, revision):
220
219
221 method = request.GET.get('diff', 'show')
220 method = request.GET.get('diff', 'show')
222 try:
221 try:
223 c.scm_type = c.rhodecode_repo.alias
222 c.scm_type = c.rhodecode_repo.alias
224 c.changeset = c.rhodecode_repo.get_changeset(revision)
223 c.changeset = c.rhodecode_repo.get_changeset(revision)
225 except RepositoryError:
224 except RepositoryError:
226 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
227 return redirect(url('home'))
226 return redirect(url('home'))
228 else:
227 else:
229 try:
228 try:
230 c.changeset_parent = c.changeset.parents[0]
229 c.changeset_parent = c.changeset.parents[0]
231 except IndexError:
230 except IndexError:
232 c.changeset_parent = None
231 c.changeset_parent = None
233 c.changes = []
232 c.changes = []
234
233
235 for node in c.changeset.added:
234 for node in c.changeset.added:
236 filenode_old = FileNode(node.path, '')
235 filenode_old = FileNode(node.path, '')
237 if filenode_old.is_binary or node.is_binary:
236 if filenode_old.is_binary or node.is_binary:
238 diff = _('binary file') + '\n'
237 diff = _('binary file') + '\n'
239 else:
238 else:
240 f_gitdiff = differ.get_gitdiff(filenode_old, node)
239 f_gitdiff = differ.get_gitdiff(filenode_old, node)
241 diff = differ.DiffProcessor(f_gitdiff,
240 diff = differ.DiffProcessor(f_gitdiff,
242 format='gitdiff').raw_diff()
241 format='gitdiff').raw_diff()
243
242
244 cs1 = None
243 cs1 = None
245 cs2 = node.last_changeset.raw_id
244 cs2 = node.last_changeset.raw_id
246 c.changes.append(('added', node, diff, cs1, cs2))
245 c.changes.append(('added', node, diff, cs1, cs2))
247
246
248 for node in c.changeset.changed:
247 for node in c.changeset.changed:
249 filenode_old = c.changeset_parent.get_node(node.path)
248 filenode_old = c.changeset_parent.get_node(node.path)
250 if filenode_old.is_binary or node.is_binary:
249 if filenode_old.is_binary or node.is_binary:
251 diff = _('binary file')
250 diff = _('binary file')
252 else:
251 else:
253 f_gitdiff = differ.get_gitdiff(filenode_old, node)
252 f_gitdiff = differ.get_gitdiff(filenode_old, node)
254 diff = differ.DiffProcessor(f_gitdiff,
253 diff = differ.DiffProcessor(f_gitdiff,
255 format='gitdiff').raw_diff()
254 format='gitdiff').raw_diff()
256
255
257 cs1 = filenode_old.last_changeset.raw_id
256 cs1 = filenode_old.last_changeset.raw_id
258 cs2 = node.last_changeset.raw_id
257 cs2 = node.last_changeset.raw_id
259 c.changes.append(('changed', node, diff, cs1, cs2))
258 c.changes.append(('changed', node, diff, cs1, cs2))
260
259
261 response.content_type = 'text/plain'
260 response.content_type = 'text/plain'
262
261
263 if method == 'download':
262 if method == 'download':
264 response.content_disposition = 'attachment; filename=%s.patch' \
263 response.content_disposition = 'attachment; filename=%s.patch' \
265 % revision
264 % revision
266
265
267 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id for x in
266 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id for x in
268 c.changeset.parents])
267 c.changeset.parents])
269
268
270 c.diffs = ''
269 c.diffs = ''
271 for x in c.changes:
270 for x in c.changes:
272 c.diffs += x[2]
271 c.diffs += x[2]
273
272
274 return render('changeset/raw_changeset.html')
273 return render('changeset/raw_changeset.html')
275
274
276 def comment(self, repo_name, revision):
275 def comment(self, repo_name, revision):
277 ccmodel = ChangesetCommentsModel()
276 ChangesetCommentsModel().create(text=request.POST.get('text'),
278
277 repo_id=c.rhodecode_db_repo.repo_id,
279 ccmodel.create(text=request.POST.get('text'),
278 user_id=c.rhodecode_user.user_id,
280 repo_id=c.rhodecode_db_repo.repo_id,
279 revision=revision,
281 user_id=c.rhodecode_user.user_id,
280 f_path=request.POST.get('f_path'),
282 revision=revision, f_path=request.POST.get('f_path'),
281 line_no=request.POST.get('line'))
283 line_no=request.POST.get('line'))
284
282
285 return redirect(h.url('changeset_home', repo_name=repo_name,
283 return redirect(h.url('changeset_home', repo_name=repo_name,
286 revision=revision))
284 revision=revision))
287
285
288 @jsonify
286 @jsonify
289 def delete_comment(self, comment_id):
287 def delete_comment(self, comment_id):
290 co = ChangesetComment.get(comment_id)
288 co = ChangesetComment.get(comment_id)
291 if (h.HasPermissionAny('hg.admin', 'repository.admin')() or
289 owner = lambda : co.author.user_id == c.rhodecode_user.user_id
292 co.author.user_id == c.rhodecode_user.user_id):
290 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
293 ccmodel = ChangesetCommentsModel()
291 ccmodel = ChangesetCommentsModel()
294 ccmodel.delete(comment_id=comment_id)
292 ccmodel.delete(comment_id=comment_id)
295 return True
293 return True
296 else:
294 else:
297 raise HTTPForbidden()
295 raise HTTPForbidden()
298
296
@@ -1,107 +1,129 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.comment
3 rhodecode.model.comment
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 comments model for RhodeCode
6 comments model for RhodeCode
7
7
8 :created_on: Nov 11, 2011
8 :created_on: Nov 11, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26
26 import re
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons.i18n.translation import _
31 from sqlalchemy.util.compat import defaultdict
32
33 from rhodecode.lib import helpers as h
30 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
31 from rhodecode.model.db import ChangesetComment, User, Notification
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
32 from sqlalchemy.util.compat import defaultdict
33 from rhodecode.model.notification import NotificationModel
36 from rhodecode.model.notification import NotificationModel
34
37
35 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
36
39
37
40
38 class ChangesetCommentsModel(BaseModel):
41 class ChangesetCommentsModel(BaseModel):
39
42
40
43
44 def _extract_mentions(self, s):
45 usrs = []
46 for username in re.findall(r'(?:^@|\s@)(\w+)', s):
47 user_obj = User.get_by_username(username, case_insensitive=True)
48 if user_obj:
49 usrs.append(user_obj)
50
51 return usrs
52
41 def create(self, text, repo_id, user_id, revision, f_path=None,
53 def create(self, text, repo_id, user_id, revision, f_path=None,
42 line_no=None):
54 line_no=None):
43 """
55 """
44 Creates new comment for changeset
56 Creates new comment for changeset
45
57
46 :param text:
58 :param text:
47 :param repo_id:
59 :param repo_id:
48 :param user_id:
60 :param user_id:
49 :param revision:
61 :param revision:
50 :param f_path:
62 :param f_path:
51 :param line_no:
63 :param line_no:
52 """
64 """
53 if text:
65 if text:
66 repo = Repository.get(repo_id)
67 desc = repo.scm_instance.get_changeset(revision).message
54 comment = ChangesetComment()
68 comment = ChangesetComment()
55 comment.repo_id = repo_id
69 comment.repo = repo
56 comment.user_id = user_id
70 comment.user_id = user_id
57 comment.revision = revision
71 comment.revision = revision
58 comment.text = text
72 comment.text = text
59 comment.f_path = f_path
73 comment.f_path = f_path
60 comment.line_no = line_no
74 comment.line_no = line_no
61
75
62 self.sa.add(comment)
76 self.sa.add(comment)
63 self.sa.commit()
77 self.sa.flush()
64
78
65 # make notification
79 # make notification
66 usr = User.get(user_id)
80 line = ''
67 subj = 'User %s commented on %s' % (usr.username, revision)
81 if line_no:
82 line = _('on line %s') % line_no
83 subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
84 {'commit_desc':desc,'line':line},
85 h.url('changeset_home', repo_name=repo.repo_name,
86 revision = revision,
87 anchor = 'comment-%s' % comment.comment_id
88 )
89 )
68 body = text
90 body = text
69 recipients = ChangesetComment.get_users(revision=revision)
91 recipients = ChangesetComment.get_users(revision=revision)
92 recipients += self._extract_mentions(body)
70 NotificationModel().create(created_by=user_id, subject=subj,
93 NotificationModel().create(created_by=user_id, subject=subj,
71 body = body, recipients = recipients,
94 body = body, recipients = recipients,
72 type_ = Notification.TYPE_CHANGESET_COMMENT)
95 type_ = Notification.TYPE_CHANGESET_COMMENT)
73
96
74
75 return comment
97 return comment
76
98
77 def delete(self, comment_id):
99 def delete(self, comment_id):
78 """
100 """
79 Deletes given comment
101 Deletes given comment
80
102
81 :param comment_id:
103 :param comment_id:
82 """
104 """
83 comment = ChangesetComment.get(comment_id)
105 comment = ChangesetComment.get(comment_id)
84 self.sa.delete(comment)
106 self.sa.delete(comment)
85 self.sa.commit()
107 self.sa.commit()
86 return comment
108 return comment
87
109
88
110
89 def get_comments(self, repo_id, revision):
111 def get_comments(self, repo_id, revision):
90 return ChangesetComment.query()\
112 return ChangesetComment.query()\
91 .filter(ChangesetComment.repo_id == repo_id)\
113 .filter(ChangesetComment.repo_id == repo_id)\
92 .filter(ChangesetComment.revision == revision)\
114 .filter(ChangesetComment.revision == revision)\
93 .filter(ChangesetComment.line_no == None)\
115 .filter(ChangesetComment.line_no == None)\
94 .filter(ChangesetComment.f_path == None).all()
116 .filter(ChangesetComment.f_path == None).all()
95
117
96 def get_inline_comments(self, repo_id, revision):
118 def get_inline_comments(self, repo_id, revision):
97 comments = self.sa.query(ChangesetComment)\
119 comments = self.sa.query(ChangesetComment)\
98 .filter(ChangesetComment.repo_id == repo_id)\
120 .filter(ChangesetComment.repo_id == repo_id)\
99 .filter(ChangesetComment.revision == revision)\
121 .filter(ChangesetComment.revision == revision)\
100 .filter(ChangesetComment.line_no != None)\
122 .filter(ChangesetComment.line_no != None)\
101 .filter(ChangesetComment.f_path != None).all()
123 .filter(ChangesetComment.f_path != None).all()
102
124
103 paths = defaultdict(lambda:defaultdict(list))
125 paths = defaultdict(lambda:defaultdict(list))
104
126
105 for co in comments:
127 for co in comments:
106 paths[co.f_path][co.line_no].append(co)
128 paths[co.f_path][co.line_no].append(co)
107 return paths.items()
129 return paths.items()
@@ -1,1192 +1,1200 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from beaker.cache import cache_region, region_invalidate
36 from beaker.cache import cache_region, region_invalidate
37
37
38 from vcs import get_backend
38 from vcs import get_backend
39 from vcs.utils.helpers import get_scm
39 from vcs.utils.helpers import get_scm
40 from vcs.exceptions import VCSError
40 from vcs.exceptions import VCSError
41 from vcs.utils.lazy import LazyProperty
41 from vcs.utils.lazy import LazyProperty
42
42
43 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
43 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
44 generate_api_key, safe_unicode
44 generate_api_key, safe_unicode
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib.compat import json
46 from rhodecode.lib.compat import json
47 from rhodecode.lib.caching_query import FromCache
47 from rhodecode.lib.caching_query import FromCache
48
48
49 from rhodecode.model.meta import Base, Session
49 from rhodecode.model.meta import Base, Session
50
50
51
51
52
53 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
54
53
55 #==============================================================================
54 #==============================================================================
56 # BASE CLASSES
55 # BASE CLASSES
57 #==============================================================================
56 #==============================================================================
58
57
59 class ModelSerializer(json.JSONEncoder):
58 class ModelSerializer(json.JSONEncoder):
60 """
59 """
61 Simple Serializer for JSON,
60 Simple Serializer for JSON,
62
61
63 usage::
62 usage::
64
63
65 to make object customized for serialization implement a __json__
64 to make object customized for serialization implement a __json__
66 method that will return a dict for serialization into json
65 method that will return a dict for serialization into json
67
66
68 example::
67 example::
69
68
70 class Task(object):
69 class Task(object):
71
70
72 def __init__(self, name, value):
71 def __init__(self, name, value):
73 self.name = name
72 self.name = name
74 self.value = value
73 self.value = value
75
74
76 def __json__(self):
75 def __json__(self):
77 return dict(name=self.name,
76 return dict(name=self.name,
78 value=self.value)
77 value=self.value)
79
78
80 """
79 """
81
80
82 def default(self, obj):
81 def default(self, obj):
83
82
84 if hasattr(obj, '__json__'):
83 if hasattr(obj, '__json__'):
85 return obj.__json__()
84 return obj.__json__()
86 else:
85 else:
87 return json.JSONEncoder.default(self, obj)
86 return json.JSONEncoder.default(self, obj)
88
87
89 class BaseModel(object):
88 class BaseModel(object):
90 """Base Model for all classess
89 """Base Model for all classess
91
90
92 """
91 """
93
92
94 @classmethod
93 @classmethod
95 def _get_keys(cls):
94 def _get_keys(cls):
96 """return column names for this model """
95 """return column names for this model """
97 return class_mapper(cls).c.keys()
96 return class_mapper(cls).c.keys()
98
97
99 def get_dict(self):
98 def get_dict(self):
100 """return dict with keys and values corresponding
99 """return dict with keys and values corresponding
101 to this model data """
100 to this model data """
102
101
103 d = {}
102 d = {}
104 for k in self._get_keys():
103 for k in self._get_keys():
105 d[k] = getattr(self, k)
104 d[k] = getattr(self, k)
106 return d
105 return d
107
106
108 def get_appstruct(self):
107 def get_appstruct(self):
109 """return list with keys and values tupples corresponding
108 """return list with keys and values tupples corresponding
110 to this model data """
109 to this model data """
111
110
112 l = []
111 l = []
113 for k in self._get_keys():
112 for k in self._get_keys():
114 l.append((k, getattr(self, k),))
113 l.append((k, getattr(self, k),))
115 return l
114 return l
116
115
117 def populate_obj(self, populate_dict):
116 def populate_obj(self, populate_dict):
118 """populate model with data from given populate_dict"""
117 """populate model with data from given populate_dict"""
119
118
120 for k in self._get_keys():
119 for k in self._get_keys():
121 if k in populate_dict:
120 if k in populate_dict:
122 setattr(self, k, populate_dict[k])
121 setattr(self, k, populate_dict[k])
123
122
124 @classmethod
123 @classmethod
125 def query(cls):
124 def query(cls):
126 return Session.query(cls)
125 return Session.query(cls)
127
126
128 @classmethod
127 @classmethod
129 def get(cls, id_):
128 def get(cls, id_):
130 if id_:
129 if id_:
131 return cls.query().get(id_)
130 return cls.query().get(id_)
132
131
133 @classmethod
132 @classmethod
134 def getAll(cls):
133 def getAll(cls):
135 return cls.query().all()
134 return cls.query().all()
136
135
137 @classmethod
136 @classmethod
138 def delete(cls, id_):
137 def delete(cls, id_):
139 obj = cls.query().get(id_)
138 obj = cls.query().get(id_)
140 Session.delete(obj)
139 Session.delete(obj)
141 Session.commit()
140 Session.commit()
142
141
143
142
144 class RhodeCodeSetting(Base, BaseModel):
143 class RhodeCodeSetting(Base, BaseModel):
145 __tablename__ = 'rhodecode_settings'
144 __tablename__ = 'rhodecode_settings'
146 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
145 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
147 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
146 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
148 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150
149
151 def __init__(self, k='', v=''):
150 def __init__(self, k='', v=''):
152 self.app_settings_name = k
151 self.app_settings_name = k
153 self.app_settings_value = v
152 self.app_settings_value = v
154
153
155
154
156 @validates('_app_settings_value')
155 @validates('_app_settings_value')
157 def validate_settings_value(self, key, val):
156 def validate_settings_value(self, key, val):
158 assert type(val) == unicode
157 assert type(val) == unicode
159 return val
158 return val
160
159
161 @hybrid_property
160 @hybrid_property
162 def app_settings_value(self):
161 def app_settings_value(self):
163 v = self._app_settings_value
162 v = self._app_settings_value
164 if v == 'ldap_active':
163 if v == 'ldap_active':
165 v = str2bool(v)
164 v = str2bool(v)
166 return v
165 return v
167
166
168 @app_settings_value.setter
167 @app_settings_value.setter
169 def app_settings_value(self, val):
168 def app_settings_value(self, val):
170 """
169 """
171 Setter that will always make sure we use unicode in app_settings_value
170 Setter that will always make sure we use unicode in app_settings_value
172
171
173 :param val:
172 :param val:
174 """
173 """
175 self._app_settings_value = safe_unicode(val)
174 self._app_settings_value = safe_unicode(val)
176
175
177 def __repr__(self):
176 def __repr__(self):
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
177 return "<%s('%s:%s')>" % (self.__class__.__name__,
179 self.app_settings_name, self.app_settings_value)
178 self.app_settings_name, self.app_settings_value)
180
179
181
180
182 @classmethod
181 @classmethod
183 def get_by_name(cls, ldap_key):
182 def get_by_name(cls, ldap_key):
184 return cls.query()\
183 return cls.query()\
185 .filter(cls.app_settings_name == ldap_key).scalar()
184 .filter(cls.app_settings_name == ldap_key).scalar()
186
185
187 @classmethod
186 @classmethod
188 def get_app_settings(cls, cache=False):
187 def get_app_settings(cls, cache=False):
189
188
190 ret = cls.query()
189 ret = cls.query()
191
190
192 if cache:
191 if cache:
193 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
192 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
194
193
195 if not ret:
194 if not ret:
196 raise Exception('Could not get application settings !')
195 raise Exception('Could not get application settings !')
197 settings = {}
196 settings = {}
198 for each in ret:
197 for each in ret:
199 settings['rhodecode_' + each.app_settings_name] = \
198 settings['rhodecode_' + each.app_settings_name] = \
200 each.app_settings_value
199 each.app_settings_value
201
200
202 return settings
201 return settings
203
202
204 @classmethod
203 @classmethod
205 def get_ldap_settings(cls, cache=False):
204 def get_ldap_settings(cls, cache=False):
206 ret = cls.query()\
205 ret = cls.query()\
207 .filter(cls.app_settings_name.startswith('ldap_')).all()
206 .filter(cls.app_settings_name.startswith('ldap_')).all()
208 fd = {}
207 fd = {}
209 for row in ret:
208 for row in ret:
210 fd.update({row.app_settings_name:row.app_settings_value})
209 fd.update({row.app_settings_name:row.app_settings_value})
211
210
212 return fd
211 return fd
213
212
214
213
215 class RhodeCodeUi(Base, BaseModel):
214 class RhodeCodeUi(Base, BaseModel):
216 __tablename__ = 'rhodecode_ui'
215 __tablename__ = 'rhodecode_ui'
217 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
216 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
218
217
219 HOOK_UPDATE = 'changegroup.update'
218 HOOK_UPDATE = 'changegroup.update'
220 HOOK_REPO_SIZE = 'changegroup.repo_size'
219 HOOK_REPO_SIZE = 'changegroup.repo_size'
221 HOOK_PUSH = 'pretxnchangegroup.push_logger'
220 HOOK_PUSH = 'pretxnchangegroup.push_logger'
222 HOOK_PULL = 'preoutgoing.pull_logger'
221 HOOK_PULL = 'preoutgoing.pull_logger'
223
222
224 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
223 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
225 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
224 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
227 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
229
228
230
229
231 @classmethod
230 @classmethod
232 def get_by_key(cls, key):
231 def get_by_key(cls, key):
233 return cls.query().filter(cls.ui_key == key)
232 return cls.query().filter(cls.ui_key == key)
234
233
235
234
236 @classmethod
235 @classmethod
237 def get_builtin_hooks(cls):
236 def get_builtin_hooks(cls):
238 q = cls.query()
237 q = cls.query()
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
238 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
240 cls.HOOK_REPO_SIZE,
239 cls.HOOK_REPO_SIZE,
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
240 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 return q.all()
241 return q.all()
243
242
244 @classmethod
243 @classmethod
245 def get_custom_hooks(cls):
244 def get_custom_hooks(cls):
246 q = cls.query()
245 q = cls.query()
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
246 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
248 cls.HOOK_REPO_SIZE,
247 cls.HOOK_REPO_SIZE,
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
248 cls.HOOK_PUSH, cls.HOOK_PULL]))
250 q = q.filter(cls.ui_section == 'hooks')
249 q = q.filter(cls.ui_section == 'hooks')
251 return q.all()
250 return q.all()
252
251
253 @classmethod
252 @classmethod
254 def create_or_update_hook(cls, key, val):
253 def create_or_update_hook(cls, key, val):
255 new_ui = cls.get_by_key(key).scalar() or cls()
254 new_ui = cls.get_by_key(key).scalar() or cls()
256 new_ui.ui_section = 'hooks'
255 new_ui.ui_section = 'hooks'
257 new_ui.ui_active = True
256 new_ui.ui_active = True
258 new_ui.ui_key = key
257 new_ui.ui_key = key
259 new_ui.ui_value = val
258 new_ui.ui_value = val
260
259
261 Session.add(new_ui)
260 Session.add(new_ui)
262 Session.commit()
261 Session.commit()
263
262
264
263
265 class User(Base, BaseModel):
264 class User(Base, BaseModel):
266 __tablename__ = 'users'
265 __tablename__ = 'users'
267 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
266 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
268 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
269 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
270 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
272 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
271 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
273 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
275 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
277 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279
278
280 user_log = relationship('UserLog', cascade='all')
279 user_log = relationship('UserLog', cascade='all')
281 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
280 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
282
281
283 repositories = relationship('Repository')
282 repositories = relationship('Repository')
284 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
283 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
285 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
284 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
286
285
287 group_member = relationship('UsersGroupMember', cascade='all')
286 group_member = relationship('UsersGroupMember', cascade='all')
288
287
289 notifications = relationship('Notification', secondary='user_to_notification')
288 notifications = relationship('Notification',
289 secondary='user_to_notification',
290 order_by=lambda :Notification.created_on.desc())
290
291
291 @property
292 @property
292 def full_contact(self):
293 def full_contact(self):
293 return '%s %s <%s>' % (self.name, self.lastname, self.email)
294 return '%s %s <%s>' % (self.name, self.lastname, self.email)
294
295
295 @property
296 @property
296 def short_contact(self):
297 def short_contact(self):
297 return '%s %s' % (self.name, self.lastname)
298 return '%s %s' % (self.name, self.lastname)
298
299
299 @property
300 @property
300 def is_admin(self):
301 def is_admin(self):
301 return self.admin
302 return self.admin
302
303
303 def __repr__(self):
304 def __repr__(self):
304 try:
305 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
305 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
306 self.user_id, self.username)
306 self.user_id, self.username)
307
307 except:
308 return self.__class__.__name__
309
308
310 @classmethod
309 @classmethod
311 def get_by_username(cls, username, case_insensitive=False, cache=False):
310 def get_by_username(cls, username, case_insensitive=False, cache=False):
312 if case_insensitive:
311 if case_insensitive:
313 q = cls.query().filter(cls.username.ilike(username))
312 q = cls.query().filter(cls.username.ilike(username))
314 else:
313 else:
315 q = cls.query().filter(cls.username == username)
314 q = cls.query().filter(cls.username == username)
316
315
317 if cache:
316 if cache:
318 q = q.options(FromCache("sql_cache_short",
317 q = q.options(FromCache("sql_cache_short",
319 "get_user_%s" % username))
318 "get_user_%s" % username))
320 return q.scalar()
319 return q.scalar()
321
320
322 @classmethod
321 @classmethod
323 def get_by_api_key(cls, api_key, cache=False):
322 def get_by_api_key(cls, api_key, cache=False):
324 q = cls.query().filter(cls.api_key == api_key)
323 q = cls.query().filter(cls.api_key == api_key)
325
324
326 if cache:
325 if cache:
327 q = q.options(FromCache("sql_cache_short",
326 q = q.options(FromCache("sql_cache_short",
328 "get_api_key_%s" % api_key))
327 "get_api_key_%s" % api_key))
329 return q.scalar()
328 return q.scalar()
330
329
331 def update_lastlogin(self):
330 def update_lastlogin(self):
332 """Update user lastlogin"""
331 """Update user lastlogin"""
333
332
334 self.last_login = datetime.datetime.now()
333 self.last_login = datetime.datetime.now()
335 Session.add(self)
334 Session.add(self)
336 Session.commit()
335 Session.commit()
337 log.debug('updated user %s lastlogin', self.username)
336 log.debug('updated user %s lastlogin', self.username)
338
337
338
339 class UserLog(Base, BaseModel):
339 class UserLog(Base, BaseModel):
340 __tablename__ = 'user_logs'
340 __tablename__ = 'user_logs'
341 __table_args__ = {'extend_existing':True}
341 __table_args__ = {'extend_existing':True}
342 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
342 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
343 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
343 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
344 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
344 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
345 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
345 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
347 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
347 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
348 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
348 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
349
349
350 @property
350 @property
351 def action_as_day(self):
351 def action_as_day(self):
352 return date(*self.action_date.timetuple()[:3])
352 return date(*self.action_date.timetuple()[:3])
353
353
354 user = relationship('User')
354 user = relationship('User')
355 repository = relationship('Repository')
355 repository = relationship('Repository')
356
356
357
357
358 class UsersGroup(Base, BaseModel):
358 class UsersGroup(Base, BaseModel):
359 __tablename__ = 'users_groups'
359 __tablename__ = 'users_groups'
360 __table_args__ = {'extend_existing':True}
360 __table_args__ = {'extend_existing':True}
361
361
362 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
362 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
363 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
363 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
364 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
364 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
365
365
366 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
366 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
367
367
368 def __repr__(self):
368 def __repr__(self):
369 return '<userGroup(%s)>' % (self.users_group_name)
369 return '<userGroup(%s)>' % (self.users_group_name)
370
370
371 @classmethod
371 @classmethod
372 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
372 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
373 if case_insensitive:
373 if case_insensitive:
374 gr = cls.query()\
374 gr = cls.query()\
375 .filter(cls.users_group_name.ilike(group_name))
375 .filter(cls.users_group_name.ilike(group_name))
376 else:
376 else:
377 gr = cls.query()\
377 gr = cls.query()\
378 .filter(cls.users_group_name == group_name)
378 .filter(cls.users_group_name == group_name)
379 if cache:
379 if cache:
380 gr = gr.options(FromCache("sql_cache_short",
380 gr = gr.options(FromCache("sql_cache_short",
381 "get_user_%s" % group_name))
381 "get_user_%s" % group_name))
382 return gr.scalar()
382 return gr.scalar()
383
383
384
384
385 @classmethod
385 @classmethod
386 def get(cls, users_group_id, cache=False):
386 def get(cls, users_group_id, cache=False):
387 users_group = cls.query()
387 users_group = cls.query()
388 if cache:
388 if cache:
389 users_group = users_group.options(FromCache("sql_cache_short",
389 users_group = users_group.options(FromCache("sql_cache_short",
390 "get_users_group_%s" % users_group_id))
390 "get_users_group_%s" % users_group_id))
391 return users_group.get(users_group_id)
391 return users_group.get(users_group_id)
392
392
393 @classmethod
393 @classmethod
394 def create(cls, form_data):
394 def create(cls, form_data):
395 try:
395 try:
396 new_users_group = cls()
396 new_users_group = cls()
397 for k, v in form_data.items():
397 for k, v in form_data.items():
398 setattr(new_users_group, k, v)
398 setattr(new_users_group, k, v)
399
399
400 Session.add(new_users_group)
400 Session.add(new_users_group)
401 Session.commit()
401 Session.commit()
402 return new_users_group
402 return new_users_group
403 except:
403 except:
404 log.error(traceback.format_exc())
404 log.error(traceback.format_exc())
405 Session.rollback()
405 Session.rollback()
406 raise
406 raise
407
407
408 @classmethod
408 @classmethod
409 def update(cls, users_group_id, form_data):
409 def update(cls, users_group_id, form_data):
410
410
411 try:
411 try:
412 users_group = cls.get(users_group_id, cache=False)
412 users_group = cls.get(users_group_id, cache=False)
413
413
414 for k, v in form_data.items():
414 for k, v in form_data.items():
415 if k == 'users_group_members':
415 if k == 'users_group_members':
416 users_group.members = []
416 users_group.members = []
417 Session.flush()
417 Session.flush()
418 members_list = []
418 members_list = []
419 if v:
419 if v:
420 v = [v] if isinstance(v, basestring) else v
420 v = [v] if isinstance(v, basestring) else v
421 for u_id in set(v):
421 for u_id in set(v):
422 member = UsersGroupMember(users_group_id, u_id)
422 member = UsersGroupMember(users_group_id, u_id)
423 members_list.append(member)
423 members_list.append(member)
424 setattr(users_group, 'members', members_list)
424 setattr(users_group, 'members', members_list)
425 setattr(users_group, k, v)
425 setattr(users_group, k, v)
426
426
427 Session.add(users_group)
427 Session.add(users_group)
428 Session.commit()
428 Session.commit()
429 except:
429 except:
430 log.error(traceback.format_exc())
430 log.error(traceback.format_exc())
431 Session.rollback()
431 Session.rollback()
432 raise
432 raise
433
433
434 @classmethod
434 @classmethod
435 def delete(cls, users_group_id):
435 def delete(cls, users_group_id):
436 try:
436 try:
437
437
438 # check if this group is not assigned to repo
438 # check if this group is not assigned to repo
439 assigned_groups = UsersGroupRepoToPerm.query()\
439 assigned_groups = UsersGroupRepoToPerm.query()\
440 .filter(UsersGroupRepoToPerm.users_group_id ==
440 .filter(UsersGroupRepoToPerm.users_group_id ==
441 users_group_id).all()
441 users_group_id).all()
442
442
443 if assigned_groups:
443 if assigned_groups:
444 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
444 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
445 assigned_groups)
445 assigned_groups)
446
446
447 users_group = cls.get(users_group_id, cache=False)
447 users_group = cls.get(users_group_id, cache=False)
448 Session.delete(users_group)
448 Session.delete(users_group)
449 Session.commit()
449 Session.commit()
450 except:
450 except:
451 log.error(traceback.format_exc())
451 log.error(traceback.format_exc())
452 Session.rollback()
452 Session.rollback()
453 raise
453 raise
454
454
455 class UsersGroupMember(Base, BaseModel):
455 class UsersGroupMember(Base, BaseModel):
456 __tablename__ = 'users_groups_members'
456 __tablename__ = 'users_groups_members'
457 __table_args__ = {'extend_existing':True}
457 __table_args__ = {'extend_existing':True}
458
458
459 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
459 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
460 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
460 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
461 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
461 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
462
462
463 user = relationship('User', lazy='joined')
463 user = relationship('User', lazy='joined')
464 users_group = relationship('UsersGroup')
464 users_group = relationship('UsersGroup')
465
465
466 def __init__(self, gr_id='', u_id=''):
466 def __init__(self, gr_id='', u_id=''):
467 self.users_group_id = gr_id
467 self.users_group_id = gr_id
468 self.user_id = u_id
468 self.user_id = u_id
469
469
470 @staticmethod
470 @staticmethod
471 def add_user_to_group(group, user):
471 def add_user_to_group(group, user):
472 ugm = UsersGroupMember()
472 ugm = UsersGroupMember()
473 ugm.users_group = group
473 ugm.users_group = group
474 ugm.user = user
474 ugm.user = user
475 Session.add(ugm)
475 Session.add(ugm)
476 Session.commit()
476 Session.commit()
477 return ugm
477 return ugm
478
478
479 class Repository(Base, BaseModel):
479 class Repository(Base, BaseModel):
480 __tablename__ = 'repositories'
480 __tablename__ = 'repositories'
481 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
481 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
482
482
483 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
483 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
484 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
484 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
485 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
485 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
486 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
486 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
488 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
488 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
489 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
489 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
490 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
490 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
491 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
491 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
492 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
492 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
493
493
494 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
494 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
495 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
495 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
496
496
497
497
498 user = relationship('User')
498 user = relationship('User')
499 fork = relationship('Repository', remote_side=repo_id)
499 fork = relationship('Repository', remote_side=repo_id)
500 group = relationship('RepoGroup')
500 group = relationship('RepoGroup')
501 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
501 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
502 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
502 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
503 stats = relationship('Statistics', cascade='all', uselist=False)
503 stats = relationship('Statistics', cascade='all', uselist=False)
504
504
505 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
505 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
506
506
507 logs = relationship('UserLog', cascade='all')
507 logs = relationship('UserLog', cascade='all')
508
508
509 def __repr__(self):
509 def __repr__(self):
510 return "<%s('%s:%s')>" % (self.__class__.__name__,
510 return "<%s('%s:%s')>" % (self.__class__.__name__,
511 self.repo_id, self.repo_name)
511 self.repo_id, self.repo_name)
512
512
513 @classmethod
513 @classmethod
514 def url_sep(cls):
514 def url_sep(cls):
515 return '/'
515 return '/'
516
516
517 @classmethod
517 @classmethod
518 def get_by_repo_name(cls, repo_name):
518 def get_by_repo_name(cls, repo_name):
519 q = Session.query(cls).filter(cls.repo_name == repo_name)
519 q = Session.query(cls).filter(cls.repo_name == repo_name)
520 q = q.options(joinedload(Repository.fork))\
520 q = q.options(joinedload(Repository.fork))\
521 .options(joinedload(Repository.user))\
521 .options(joinedload(Repository.user))\
522 .options(joinedload(Repository.group))
522 .options(joinedload(Repository.group))
523 return q.one()
523 return q.one()
524
524
525 @classmethod
525 @classmethod
526 def get_repo_forks(cls, repo_id):
526 def get_repo_forks(cls, repo_id):
527 return cls.query().filter(Repository.fork_id == repo_id)
527 return cls.query().filter(Repository.fork_id == repo_id)
528
528
529 @classmethod
529 @classmethod
530 def base_path(cls):
530 def base_path(cls):
531 """
531 """
532 Returns base path when all repos are stored
532 Returns base path when all repos are stored
533
533
534 :param cls:
534 :param cls:
535 """
535 """
536 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
536 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
537 cls.url_sep())
537 cls.url_sep())
538 q.options(FromCache("sql_cache_short", "repository_repo_path"))
538 q.options(FromCache("sql_cache_short", "repository_repo_path"))
539 return q.one().ui_value
539 return q.one().ui_value
540
540
541 @property
541 @property
542 def just_name(self):
542 def just_name(self):
543 return self.repo_name.split(Repository.url_sep())[-1]
543 return self.repo_name.split(Repository.url_sep())[-1]
544
544
545 @property
545 @property
546 def groups_with_parents(self):
546 def groups_with_parents(self):
547 groups = []
547 groups = []
548 if self.group is None:
548 if self.group is None:
549 return groups
549 return groups
550
550
551 cur_gr = self.group
551 cur_gr = self.group
552 groups.insert(0, cur_gr)
552 groups.insert(0, cur_gr)
553 while 1:
553 while 1:
554 gr = getattr(cur_gr, 'parent_group', None)
554 gr = getattr(cur_gr, 'parent_group', None)
555 cur_gr = cur_gr.parent_group
555 cur_gr = cur_gr.parent_group
556 if gr is None:
556 if gr is None:
557 break
557 break
558 groups.insert(0, gr)
558 groups.insert(0, gr)
559
559
560 return groups
560 return groups
561
561
562 @property
562 @property
563 def groups_and_repo(self):
563 def groups_and_repo(self):
564 return self.groups_with_parents, self.just_name
564 return self.groups_with_parents, self.just_name
565
565
566 @LazyProperty
566 @LazyProperty
567 def repo_path(self):
567 def repo_path(self):
568 """
568 """
569 Returns base full path for that repository means where it actually
569 Returns base full path for that repository means where it actually
570 exists on a filesystem
570 exists on a filesystem
571 """
571 """
572 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
572 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
573 Repository.url_sep())
573 Repository.url_sep())
574 q.options(FromCache("sql_cache_short", "repository_repo_path"))
574 q.options(FromCache("sql_cache_short", "repository_repo_path"))
575 return q.one().ui_value
575 return q.one().ui_value
576
576
577 @property
577 @property
578 def repo_full_path(self):
578 def repo_full_path(self):
579 p = [self.repo_path]
579 p = [self.repo_path]
580 # we need to split the name by / since this is how we store the
580 # we need to split the name by / since this is how we store the
581 # names in the database, but that eventually needs to be converted
581 # names in the database, but that eventually needs to be converted
582 # into a valid system path
582 # into a valid system path
583 p += self.repo_name.split(Repository.url_sep())
583 p += self.repo_name.split(Repository.url_sep())
584 return os.path.join(*p)
584 return os.path.join(*p)
585
585
586 def get_new_name(self, repo_name):
586 def get_new_name(self, repo_name):
587 """
587 """
588 returns new full repository name based on assigned group and new new
588 returns new full repository name based on assigned group and new new
589
589
590 :param group_name:
590 :param group_name:
591 """
591 """
592 path_prefix = self.group.full_path_splitted if self.group else []
592 path_prefix = self.group.full_path_splitted if self.group else []
593 return Repository.url_sep().join(path_prefix + [repo_name])
593 return Repository.url_sep().join(path_prefix + [repo_name])
594
594
595 @property
595 @property
596 def _ui(self):
596 def _ui(self):
597 """
597 """
598 Creates an db based ui object for this repository
598 Creates an db based ui object for this repository
599 """
599 """
600 from mercurial import ui
600 from mercurial import ui
601 from mercurial import config
601 from mercurial import config
602 baseui = ui.ui()
602 baseui = ui.ui()
603
603
604 #clean the baseui object
604 #clean the baseui object
605 baseui._ocfg = config.config()
605 baseui._ocfg = config.config()
606 baseui._ucfg = config.config()
606 baseui._ucfg = config.config()
607 baseui._tcfg = config.config()
607 baseui._tcfg = config.config()
608
608
609
609
610 ret = RhodeCodeUi.query()\
610 ret = RhodeCodeUi.query()\
611 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
611 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
612
612
613 hg_ui = ret
613 hg_ui = ret
614 for ui_ in hg_ui:
614 for ui_ in hg_ui:
615 if ui_.ui_active:
615 if ui_.ui_active:
616 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
616 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
617 ui_.ui_key, ui_.ui_value)
617 ui_.ui_key, ui_.ui_value)
618 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
618 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
619
619
620 return baseui
620 return baseui
621
621
622 @classmethod
622 @classmethod
623 def is_valid(cls, repo_name):
623 def is_valid(cls, repo_name):
624 """
624 """
625 returns True if given repo name is a valid filesystem repository
625 returns True if given repo name is a valid filesystem repository
626
626
627 @param cls:
627 @param cls:
628 @param repo_name:
628 @param repo_name:
629 """
629 """
630 from rhodecode.lib.utils import is_valid_repo
630 from rhodecode.lib.utils import is_valid_repo
631
631
632 return is_valid_repo(repo_name, cls.base_path())
632 return is_valid_repo(repo_name, cls.base_path())
633
633
634
634
635 #==========================================================================
635 #==========================================================================
636 # SCM PROPERTIES
636 # SCM PROPERTIES
637 #==========================================================================
637 #==========================================================================
638
638
639 def get_changeset(self, rev):
639 def get_changeset(self, rev):
640 return get_changeset_safe(self.scm_instance, rev)
640 return get_changeset_safe(self.scm_instance, rev)
641
641
642 @property
642 @property
643 def tip(self):
643 def tip(self):
644 return self.get_changeset('tip')
644 return self.get_changeset('tip')
645
645
646 @property
646 @property
647 def author(self):
647 def author(self):
648 return self.tip.author
648 return self.tip.author
649
649
650 @property
650 @property
651 def last_change(self):
651 def last_change(self):
652 return self.scm_instance.last_change
652 return self.scm_instance.last_change
653
653
654 #==========================================================================
654 #==========================================================================
655 # SCM CACHE INSTANCE
655 # SCM CACHE INSTANCE
656 #==========================================================================
656 #==========================================================================
657
657
658 @property
658 @property
659 def invalidate(self):
659 def invalidate(self):
660 return CacheInvalidation.invalidate(self.repo_name)
660 return CacheInvalidation.invalidate(self.repo_name)
661
661
662 def set_invalidate(self):
662 def set_invalidate(self):
663 """
663 """
664 set a cache for invalidation for this instance
664 set a cache for invalidation for this instance
665 """
665 """
666 CacheInvalidation.set_invalidate(self.repo_name)
666 CacheInvalidation.set_invalidate(self.repo_name)
667
667
668 @LazyProperty
668 @LazyProperty
669 def scm_instance(self):
669 def scm_instance(self):
670 return self.__get_instance()
670 return self.__get_instance()
671
671
672 @property
672 @property
673 def scm_instance_cached(self):
673 def scm_instance_cached(self):
674 @cache_region('long_term')
674 @cache_region('long_term')
675 def _c(repo_name):
675 def _c(repo_name):
676 return self.__get_instance()
676 return self.__get_instance()
677 rn = self.repo_name
677 rn = self.repo_name
678
678
679 inv = self.invalidate
679 inv = self.invalidate
680 if inv is not None:
680 if inv is not None:
681 region_invalidate(_c, None, rn)
681 region_invalidate(_c, None, rn)
682 # update our cache
682 # update our cache
683 CacheInvalidation.set_valid(inv.cache_key)
683 CacheInvalidation.set_valid(inv.cache_key)
684 return _c(rn)
684 return _c(rn)
685
685
686 def __get_instance(self):
686 def __get_instance(self):
687
687
688 repo_full_path = self.repo_full_path
688 repo_full_path = self.repo_full_path
689
689
690 try:
690 try:
691 alias = get_scm(repo_full_path)[0]
691 alias = get_scm(repo_full_path)[0]
692 log.debug('Creating instance of %s repository', alias)
692 log.debug('Creating instance of %s repository', alias)
693 backend = get_backend(alias)
693 backend = get_backend(alias)
694 except VCSError:
694 except VCSError:
695 log.error(traceback.format_exc())
695 log.error(traceback.format_exc())
696 log.error('Perhaps this repository is in db and not in '
696 log.error('Perhaps this repository is in db and not in '
697 'filesystem run rescan repositories with '
697 'filesystem run rescan repositories with '
698 '"destroy old data " option from admin panel')
698 '"destroy old data " option from admin panel')
699 return
699 return
700
700
701 if alias == 'hg':
701 if alias == 'hg':
702
702
703 repo = backend(safe_str(repo_full_path), create=False,
703 repo = backend(safe_str(repo_full_path), create=False,
704 baseui=self._ui)
704 baseui=self._ui)
705 # skip hidden web repository
705 # skip hidden web repository
706 if repo._get_hidden():
706 if repo._get_hidden():
707 return
707 return
708 else:
708 else:
709 repo = backend(repo_full_path, create=False)
709 repo = backend(repo_full_path, create=False)
710
710
711 return repo
711 return repo
712
712
713
713
714 class RepoGroup(Base, BaseModel):
714 class RepoGroup(Base, BaseModel):
715 __tablename__ = 'groups'
715 __tablename__ = 'groups'
716 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
716 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
717 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
717 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
718 __mapper_args__ = {'order_by':'group_name'}
718 __mapper_args__ = {'order_by':'group_name'}
719
719
720 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
720 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
721 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
721 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
722 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
722 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
723 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
723 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
724
724
725 parent_group = relationship('RepoGroup', remote_side=group_id)
725 parent_group = relationship('RepoGroup', remote_side=group_id)
726
726
727
727
728 def __init__(self, group_name='', parent_group=None):
728 def __init__(self, group_name='', parent_group=None):
729 self.group_name = group_name
729 self.group_name = group_name
730 self.parent_group = parent_group
730 self.parent_group = parent_group
731
731
732 def __repr__(self):
732 def __repr__(self):
733 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
733 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
734 self.group_name)
734 self.group_name)
735
735
736 @classmethod
736 @classmethod
737 def groups_choices(cls):
737 def groups_choices(cls):
738 from webhelpers.html import literal as _literal
738 from webhelpers.html import literal as _literal
739 repo_groups = [('', '')]
739 repo_groups = [('', '')]
740 sep = ' &raquo; '
740 sep = ' &raquo; '
741 _name = lambda k: _literal(sep.join(k))
741 _name = lambda k: _literal(sep.join(k))
742
742
743 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
743 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
744 for x in cls.query().all()])
744 for x in cls.query().all()])
745
745
746 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
746 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
747 return repo_groups
747 return repo_groups
748
748
749 @classmethod
749 @classmethod
750 def url_sep(cls):
750 def url_sep(cls):
751 return '/'
751 return '/'
752
752
753 @classmethod
753 @classmethod
754 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
754 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
755 if case_insensitive:
755 if case_insensitive:
756 gr = cls.query()\
756 gr = cls.query()\
757 .filter(cls.group_name.ilike(group_name))
757 .filter(cls.group_name.ilike(group_name))
758 else:
758 else:
759 gr = cls.query()\
759 gr = cls.query()\
760 .filter(cls.group_name == group_name)
760 .filter(cls.group_name == group_name)
761 if cache:
761 if cache:
762 gr = gr.options(FromCache("sql_cache_short",
762 gr = gr.options(FromCache("sql_cache_short",
763 "get_group_%s" % group_name))
763 "get_group_%s" % group_name))
764 return gr.scalar()
764 return gr.scalar()
765
765
766 @property
766 @property
767 def parents(self):
767 def parents(self):
768 parents_recursion_limit = 5
768 parents_recursion_limit = 5
769 groups = []
769 groups = []
770 if self.parent_group is None:
770 if self.parent_group is None:
771 return groups
771 return groups
772 cur_gr = self.parent_group
772 cur_gr = self.parent_group
773 groups.insert(0, cur_gr)
773 groups.insert(0, cur_gr)
774 cnt = 0
774 cnt = 0
775 while 1:
775 while 1:
776 cnt += 1
776 cnt += 1
777 gr = getattr(cur_gr, 'parent_group', None)
777 gr = getattr(cur_gr, 'parent_group', None)
778 cur_gr = cur_gr.parent_group
778 cur_gr = cur_gr.parent_group
779 if gr is None:
779 if gr is None:
780 break
780 break
781 if cnt == parents_recursion_limit:
781 if cnt == parents_recursion_limit:
782 # this will prevent accidental infinit loops
782 # this will prevent accidental infinit loops
783 log.error('group nested more than %s' %
783 log.error('group nested more than %s' %
784 parents_recursion_limit)
784 parents_recursion_limit)
785 break
785 break
786
786
787 groups.insert(0, gr)
787 groups.insert(0, gr)
788 return groups
788 return groups
789
789
790 @property
790 @property
791 def children(self):
791 def children(self):
792 return RepoGroup.query().filter(RepoGroup.parent_group == self)
792 return RepoGroup.query().filter(RepoGroup.parent_group == self)
793
793
794 @property
794 @property
795 def name(self):
795 def name(self):
796 return self.group_name.split(RepoGroup.url_sep())[-1]
796 return self.group_name.split(RepoGroup.url_sep())[-1]
797
797
798 @property
798 @property
799 def full_path(self):
799 def full_path(self):
800 return self.group_name
800 return self.group_name
801
801
802 @property
802 @property
803 def full_path_splitted(self):
803 def full_path_splitted(self):
804 return self.group_name.split(RepoGroup.url_sep())
804 return self.group_name.split(RepoGroup.url_sep())
805
805
806 @property
806 @property
807 def repositories(self):
807 def repositories(self):
808 return Repository.query().filter(Repository.group == self)
808 return Repository.query().filter(Repository.group == self)
809
809
810 @property
810 @property
811 def repositories_recursive_count(self):
811 def repositories_recursive_count(self):
812 cnt = self.repositories.count()
812 cnt = self.repositories.count()
813
813
814 def children_count(group):
814 def children_count(group):
815 cnt = 0
815 cnt = 0
816 for child in group.children:
816 for child in group.children:
817 cnt += child.repositories.count()
817 cnt += child.repositories.count()
818 cnt += children_count(child)
818 cnt += children_count(child)
819 return cnt
819 return cnt
820
820
821 return cnt + children_count(self)
821 return cnt + children_count(self)
822
822
823
823
824 def get_new_name(self, group_name):
824 def get_new_name(self, group_name):
825 """
825 """
826 returns new full group name based on parent and new name
826 returns new full group name based on parent and new name
827
827
828 :param group_name:
828 :param group_name:
829 """
829 """
830 path_prefix = (self.parent_group.full_path_splitted if
830 path_prefix = (self.parent_group.full_path_splitted if
831 self.parent_group else [])
831 self.parent_group else [])
832 return RepoGroup.url_sep().join(path_prefix + [group_name])
832 return RepoGroup.url_sep().join(path_prefix + [group_name])
833
833
834
834
835 class Permission(Base, BaseModel):
835 class Permission(Base, BaseModel):
836 __tablename__ = 'permissions'
836 __tablename__ = 'permissions'
837 __table_args__ = {'extend_existing':True}
837 __table_args__ = {'extend_existing':True}
838 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
838 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
839 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
840 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
840 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
841
841
842 def __repr__(self):
842 def __repr__(self):
843 return "<%s('%s:%s')>" % (self.__class__.__name__,
843 return "<%s('%s:%s')>" % (self.__class__.__name__,
844 self.permission_id, self.permission_name)
844 self.permission_id, self.permission_name)
845
845
846 @classmethod
846 @classmethod
847 def get_by_key(cls, key):
847 def get_by_key(cls, key):
848 return cls.query().filter(cls.permission_name == key).scalar()
848 return cls.query().filter(cls.permission_name == key).scalar()
849
849
850 class UserRepoToPerm(Base, BaseModel):
850 class UserRepoToPerm(Base, BaseModel):
851 __tablename__ = 'repo_to_perm'
851 __tablename__ = 'repo_to_perm'
852 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
852 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
853 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
853 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
854 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
854 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
856 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
856 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
857
857
858 user = relationship('User')
858 user = relationship('User')
859 permission = relationship('Permission')
859 permission = relationship('Permission')
860 repository = relationship('Repository')
860 repository = relationship('Repository')
861
861
862 class UserToPerm(Base, BaseModel):
862 class UserToPerm(Base, BaseModel):
863 __tablename__ = 'user_to_perm'
863 __tablename__ = 'user_to_perm'
864 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
864 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
865 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
865 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
866 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
866 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
867 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
867 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
868
868
869 user = relationship('User')
869 user = relationship('User')
870 permission = relationship('Permission')
870 permission = relationship('Permission')
871
871
872 @classmethod
872 @classmethod
873 def has_perm(cls, user_id, perm):
873 def has_perm(cls, user_id, perm):
874 if not isinstance(perm, Permission):
874 if not isinstance(perm, Permission):
875 raise Exception('perm needs to be an instance of Permission class')
875 raise Exception('perm needs to be an instance of Permission class')
876
876
877 return cls.query().filter(cls.user_id == user_id)\
877 return cls.query().filter(cls.user_id == user_id)\
878 .filter(cls.permission == perm).scalar() is not None
878 .filter(cls.permission == perm).scalar() is not None
879
879
880 @classmethod
880 @classmethod
881 def grant_perm(cls, user_id, perm):
881 def grant_perm(cls, user_id, perm):
882 if not isinstance(perm, Permission):
882 if not isinstance(perm, Permission):
883 raise Exception('perm needs to be an instance of Permission class')
883 raise Exception('perm needs to be an instance of Permission class')
884
884
885 new = cls()
885 new = cls()
886 new.user_id = user_id
886 new.user_id = user_id
887 new.permission = perm
887 new.permission = perm
888 try:
888 try:
889 Session.add(new)
889 Session.add(new)
890 Session.commit()
890 Session.commit()
891 except:
891 except:
892 Session.rollback()
892 Session.rollback()
893
893
894
894
895 @classmethod
895 @classmethod
896 def revoke_perm(cls, user_id, perm):
896 def revoke_perm(cls, user_id, perm):
897 if not isinstance(perm, Permission):
897 if not isinstance(perm, Permission):
898 raise Exception('perm needs to be an instance of Permission class')
898 raise Exception('perm needs to be an instance of Permission class')
899
899
900 try:
900 try:
901 cls.query().filter(cls.user_id == user_id)\
901 cls.query().filter(cls.user_id == user_id)\
902 .filter(cls.permission == perm).delete()
902 .filter(cls.permission == perm).delete()
903 Session.commit()
903 Session.commit()
904 except:
904 except:
905 Session.rollback()
905 Session.rollback()
906
906
907 class UsersGroupRepoToPerm(Base, BaseModel):
907 class UsersGroupRepoToPerm(Base, BaseModel):
908 __tablename__ = 'users_group_repo_to_perm'
908 __tablename__ = 'users_group_repo_to_perm'
909 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
909 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
910 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
910 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
911 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
911 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
912 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
912 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
913 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
913 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
914
914
915 users_group = relationship('UsersGroup')
915 users_group = relationship('UsersGroup')
916 permission = relationship('Permission')
916 permission = relationship('Permission')
917 repository = relationship('Repository')
917 repository = relationship('Repository')
918
918
919 def __repr__(self):
919 def __repr__(self):
920 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
920 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
921
921
922 class UsersGroupToPerm(Base, BaseModel):
922 class UsersGroupToPerm(Base, BaseModel):
923 __tablename__ = 'users_group_to_perm'
923 __tablename__ = 'users_group_to_perm'
924 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
924 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
925 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
925 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
926 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
926 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
927
927
928 users_group = relationship('UsersGroup')
928 users_group = relationship('UsersGroup')
929 permission = relationship('Permission')
929 permission = relationship('Permission')
930
930
931
931
932 @classmethod
932 @classmethod
933 def has_perm(cls, users_group_id, perm):
933 def has_perm(cls, users_group_id, perm):
934 if not isinstance(perm, Permission):
934 if not isinstance(perm, Permission):
935 raise Exception('perm needs to be an instance of Permission class')
935 raise Exception('perm needs to be an instance of Permission class')
936
936
937 return cls.query().filter(cls.users_group_id ==
937 return cls.query().filter(cls.users_group_id ==
938 users_group_id)\
938 users_group_id)\
939 .filter(cls.permission == perm)\
939 .filter(cls.permission == perm)\
940 .scalar() is not None
940 .scalar() is not None
941
941
942 @classmethod
942 @classmethod
943 def grant_perm(cls, users_group_id, perm):
943 def grant_perm(cls, users_group_id, perm):
944 if not isinstance(perm, Permission):
944 if not isinstance(perm, Permission):
945 raise Exception('perm needs to be an instance of Permission class')
945 raise Exception('perm needs to be an instance of Permission class')
946
946
947 new = cls()
947 new = cls()
948 new.users_group_id = users_group_id
948 new.users_group_id = users_group_id
949 new.permission = perm
949 new.permission = perm
950 try:
950 try:
951 Session.add(new)
951 Session.add(new)
952 Session.commit()
952 Session.commit()
953 except:
953 except:
954 Session.rollback()
954 Session.rollback()
955
955
956
956
957 @classmethod
957 @classmethod
958 def revoke_perm(cls, users_group_id, perm):
958 def revoke_perm(cls, users_group_id, perm):
959 if not isinstance(perm, Permission):
959 if not isinstance(perm, Permission):
960 raise Exception('perm needs to be an instance of Permission class')
960 raise Exception('perm needs to be an instance of Permission class')
961
961
962 try:
962 try:
963 cls.query().filter(cls.users_group_id == users_group_id)\
963 cls.query().filter(cls.users_group_id == users_group_id)\
964 .filter(cls.permission == perm).delete()
964 .filter(cls.permission == perm).delete()
965 Session.commit()
965 Session.commit()
966 except:
966 except:
967 Session.rollback()
967 Session.rollback()
968
968
969
969
970 class UserRepoGroupToPerm(Base, BaseModel):
970 class UserRepoGroupToPerm(Base, BaseModel):
971 __tablename__ = 'group_to_perm'
971 __tablename__ = 'group_to_perm'
972 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
972 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
973
973
974 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
974 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
975 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
975 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
976 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
976 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
977 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
977 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
978
978
979 user = relationship('User')
979 user = relationship('User')
980 permission = relationship('Permission')
980 permission = relationship('Permission')
981 group = relationship('RepoGroup')
981 group = relationship('RepoGroup')
982
982
983 class UsersGroupRepoGroupToPerm(Base, BaseModel):
983 class UsersGroupRepoGroupToPerm(Base, BaseModel):
984 __tablename__ = 'users_group_repo_group_to_perm'
984 __tablename__ = 'users_group_repo_group_to_perm'
985 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
985 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
986
986
987 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
987 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
988 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
988 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
989 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
989 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
990 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
990 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
991
991
992 users_group = relationship('UsersGroup')
992 users_group = relationship('UsersGroup')
993 permission = relationship('Permission')
993 permission = relationship('Permission')
994 group = relationship('RepoGroup')
994 group = relationship('RepoGroup')
995
995
996 class Statistics(Base, BaseModel):
996 class Statistics(Base, BaseModel):
997 __tablename__ = 'statistics'
997 __tablename__ = 'statistics'
998 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
998 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
999 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
999 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1000 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1000 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1001 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1001 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1002 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1002 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1003 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1003 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1004 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1004 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1005
1005
1006 repository = relationship('Repository', single_parent=True)
1006 repository = relationship('Repository', single_parent=True)
1007
1007
1008 class UserFollowing(Base, BaseModel):
1008 class UserFollowing(Base, BaseModel):
1009 __tablename__ = 'user_followings'
1009 __tablename__ = 'user_followings'
1010 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1010 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1011 UniqueConstraint('user_id', 'follows_user_id')
1011 UniqueConstraint('user_id', 'follows_user_id')
1012 , {'extend_existing':True})
1012 , {'extend_existing':True})
1013
1013
1014 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1014 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1015 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1015 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1016 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1016 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1017 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1017 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1018 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1018 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1019
1019
1020 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1020 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1021
1021
1022 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1022 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1023 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1023 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1024
1024
1025
1025
1026 @classmethod
1026 @classmethod
1027 def get_repo_followers(cls, repo_id):
1027 def get_repo_followers(cls, repo_id):
1028 return cls.query().filter(cls.follows_repo_id == repo_id)
1028 return cls.query().filter(cls.follows_repo_id == repo_id)
1029
1029
1030 class CacheInvalidation(Base, BaseModel):
1030 class CacheInvalidation(Base, BaseModel):
1031 __tablename__ = 'cache_invalidation'
1031 __tablename__ = 'cache_invalidation'
1032 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1032 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1033 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1033 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1034 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1034 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1035 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1035 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1036 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1036 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1037
1037
1038
1038
1039 def __init__(self, cache_key, cache_args=''):
1039 def __init__(self, cache_key, cache_args=''):
1040 self.cache_key = cache_key
1040 self.cache_key = cache_key
1041 self.cache_args = cache_args
1041 self.cache_args = cache_args
1042 self.cache_active = False
1042 self.cache_active = False
1043
1043
1044 def __repr__(self):
1044 def __repr__(self):
1045 return "<%s('%s:%s')>" % (self.__class__.__name__,
1045 return "<%s('%s:%s')>" % (self.__class__.__name__,
1046 self.cache_id, self.cache_key)
1046 self.cache_id, self.cache_key)
1047
1047
1048 @classmethod
1048 @classmethod
1049 def invalidate(cls, key):
1049 def invalidate(cls, key):
1050 """
1050 """
1051 Returns Invalidation object if this given key should be invalidated
1051 Returns Invalidation object if this given key should be invalidated
1052 None otherwise. `cache_active = False` means that this cache
1052 None otherwise. `cache_active = False` means that this cache
1053 state is not valid and needs to be invalidated
1053 state is not valid and needs to be invalidated
1054
1054
1055 :param key:
1055 :param key:
1056 """
1056 """
1057 return cls.query()\
1057 return cls.query()\
1058 .filter(CacheInvalidation.cache_key == key)\
1058 .filter(CacheInvalidation.cache_key == key)\
1059 .filter(CacheInvalidation.cache_active == False)\
1059 .filter(CacheInvalidation.cache_active == False)\
1060 .scalar()
1060 .scalar()
1061
1061
1062 @classmethod
1062 @classmethod
1063 def set_invalidate(cls, key):
1063 def set_invalidate(cls, key):
1064 """
1064 """
1065 Mark this Cache key for invalidation
1065 Mark this Cache key for invalidation
1066
1066
1067 :param key:
1067 :param key:
1068 """
1068 """
1069
1069
1070 log.debug('marking %s for invalidation' % key)
1070 log.debug('marking %s for invalidation' % key)
1071 inv_obj = Session().query(cls)\
1071 inv_obj = Session().query(cls)\
1072 .filter(cls.cache_key == key).scalar()
1072 .filter(cls.cache_key == key).scalar()
1073 if inv_obj:
1073 if inv_obj:
1074 inv_obj.cache_active = False
1074 inv_obj.cache_active = False
1075 else:
1075 else:
1076 log.debug('cache key not found in invalidation db -> creating one')
1076 log.debug('cache key not found in invalidation db -> creating one')
1077 inv_obj = CacheInvalidation(key)
1077 inv_obj = CacheInvalidation(key)
1078
1078
1079 try:
1079 try:
1080 Session.add(inv_obj)
1080 Session.add(inv_obj)
1081 Session.commit()
1081 Session.commit()
1082 except Exception:
1082 except Exception:
1083 log.error(traceback.format_exc())
1083 log.error(traceback.format_exc())
1084 Session.rollback()
1084 Session.rollback()
1085
1085
1086 @classmethod
1086 @classmethod
1087 def set_valid(cls, key):
1087 def set_valid(cls, key):
1088 """
1088 """
1089 Mark this cache key as active and currently cached
1089 Mark this cache key as active and currently cached
1090
1090
1091 :param key:
1091 :param key:
1092 """
1092 """
1093 inv_obj = Session().query(CacheInvalidation)\
1093 inv_obj = Session().query(CacheInvalidation)\
1094 .filter(CacheInvalidation.cache_key == key).scalar()
1094 .filter(CacheInvalidation.cache_key == key).scalar()
1095 inv_obj.cache_active = True
1095 inv_obj.cache_active = True
1096 Session.add(inv_obj)
1096 Session.add(inv_obj)
1097 Session.commit()
1097 Session.commit()
1098
1098
1099
1099
1100 class ChangesetComment(Base, BaseModel):
1100 class ChangesetComment(Base, BaseModel):
1101 __tablename__ = 'changeset_comments'
1101 __tablename__ = 'changeset_comments'
1102 __table_args__ = ({'extend_existing':True},)
1102 __table_args__ = ({'extend_existing':True},)
1103 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1103 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1104 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1104 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1105 revision = Column('revision', String(40), nullable=False)
1105 revision = Column('revision', String(40), nullable=False)
1106 line_no = Column('line_no', Unicode(10), nullable=True)
1106 line_no = Column('line_no', Unicode(10), nullable=True)
1107 f_path = Column('f_path', Unicode(1000), nullable=True)
1107 f_path = Column('f_path', Unicode(1000), nullable=True)
1108 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1108 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1109 text = Column('text', Unicode(25000), nullable=False)
1109 text = Column('text', Unicode(25000), nullable=False)
1110 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1110 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1111
1111
1112 author = relationship('User', lazy='joined')
1112 author = relationship('User', lazy='joined')
1113 repo = relationship('Repository')
1113 repo = relationship('Repository')
1114
1114
1115
1115
1116 @classmethod
1116 @classmethod
1117 def get_users(cls, revision):
1117 def get_users(cls, revision):
1118 """
1118 """
1119 Returns user associated with this changesetComment. ie those
1119 Returns user associated with this changesetComment. ie those
1120 who actually commented
1120 who actually commented
1121
1121
1122 :param cls:
1122 :param cls:
1123 :param revision:
1123 :param revision:
1124 """
1124 """
1125 return Session.query(User)\
1125 return Session.query(User)\
1126 .filter(cls.revision == revision)\
1126 .filter(cls.revision == revision)\
1127 .join(ChangesetComment.author).all()
1127 .join(ChangesetComment.author).all()
1128
1128
1129
1129
1130 class Notification(Base, BaseModel):
1130 class Notification(Base, BaseModel):
1131 __tablename__ = 'notifications'
1131 __tablename__ = 'notifications'
1132 __table_args__ = ({'extend_existing':True})
1132 __table_args__ = ({'extend_existing':True})
1133
1133
1134 TYPE_CHANGESET_COMMENT = 'cs_comment'
1134 TYPE_CHANGESET_COMMENT = u'cs_comment'
1135 TYPE_MESSAGE = 'message'
1135 TYPE_MESSAGE = u'message'
1136 TYPE_MENTION = 'mention'
1136 TYPE_MENTION = u'mention'
1137
1137
1138 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1138 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1139 subject = Column('subject', Unicode(512), nullable=True)
1139 subject = Column('subject', Unicode(512), nullable=True)
1140 body = Column('body', Unicode(50000), nullable=True)
1140 body = Column('body', Unicode(50000), nullable=True)
1141 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1141 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1142 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1142 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1143 type_ = Column('type', Unicode(256))
1143 type_ = Column('type', Unicode(256))
1144
1144
1145 create_by_user = relationship('User')
1145 created_by_user = relationship('User')
1146 user_notifications = relationship('UserNotification',
1146 notifications_to_users = relationship('UserNotification',
1147 primaryjoin = 'Notification.notification_id==UserNotification.notification_id',
1147 primaryjoin='Notification.notification_id==UserNotification.notification_id',
1148 lazy='joined',
1148 cascade = "all, delete, delete-orphan")
1149 cascade = "all, delete, delete-orphan")
1149
1150
1150 @property
1151 @property
1151 def recipients(self):
1152 def recipients(self):
1152 return [x.user for x in UserNotification.query()\
1153 return [x.user for x in UserNotification.query()\
1153 .filter(UserNotification.notification == self).all()]
1154 .filter(UserNotification.notification == self).all()]
1154
1155
1155 @classmethod
1156 @classmethod
1156 def create(cls, created_by, subject, body, recipients, type_=None):
1157 def create(cls, created_by, subject, body, recipients, type_=None):
1157 if type_ is None:
1158 if type_ is None:
1158 type_ = Notification.TYPE_MESSAGE
1159 type_ = Notification.TYPE_MESSAGE
1159
1160
1160 notification = cls()
1161 notification = cls()
1161 notification.create_by_user = created_by
1162 notification.created_by_user = created_by
1162 notification.subject = subject
1163 notification.subject = subject
1163 notification.body = body
1164 notification.body = body
1164 notification.type_ = type_
1165 notification.type_ = type_
1165 Session.add(notification)
1166 Session.add(notification)
1166 for u in recipients:
1167 for u in recipients:
1167 u.notifications.append(notification)
1168 u.notifications.append(notification)
1168 Session.commit()
1169 return notification
1169 return notification
1170
1170
1171 @property
1172 def description(self):
1173 from rhodecode.model.notification import NotificationModel
1174 return NotificationModel().make_description(self)
1175
1171 class UserNotification(Base, BaseModel):
1176 class UserNotification(Base, BaseModel):
1172 __tablename__ = 'user_to_notification'
1177 __tablename__ = 'user_to_notification'
1173 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1178 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1174 {'extend_existing':True})
1179 {'extend_existing':True})
1175 user_to_notification_id = Column("user_to_notification_id", Integer(), nullable=False, unique=True, primary_key=True)
1180 user_to_notification_id = Column("user_to_notification_id", Integer(), nullable=False, unique=True, primary_key=True)
1176 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1181 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1177 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), nullable=False)
1182 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), nullable=False)
1178 read = Column('read', Boolean, default=False)
1183 read = Column('read', Boolean, default=False)
1179 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1184 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1180
1185
1181 user = relationship('User', single_parent=True, lazy="joined")
1186 user = relationship('User', single_parent=True, lazy="joined")
1182 notification = relationship('Notification',single_parent=True,
1187 notification = relationship('Notification', single_parent=True,)
1183 cascade="all, delete, delete-orphan")
1184
1188
1189 def mark_as_read(self):
1190 self.read = True
1191 Session.add(self)
1192 Session.commit()
1185
1193
1186 class DbMigrateVersion(Base, BaseModel):
1194 class DbMigrateVersion(Base, BaseModel):
1187 __tablename__ = 'db_migrate_version'
1195 __tablename__ = 'db_migrate_version'
1188 __table_args__ = {'extend_existing':True}
1196 __table_args__ = {'extend_existing':True}
1189 repository_id = Column('repository_id', String(250), primary_key=True)
1197 repository_id = Column('repository_id', String(250), primary_key=True)
1190 repository_path = Column('repository_path', Text)
1198 repository_path = Column('repository_path', Text)
1191 version = Column('version', Integer)
1199 version = Column('version', Integer)
1192
1200
@@ -1,26 +1,27 b''
1 """SQLAlchemy Metadata and Session object"""
1 """SQLAlchemy Metadata and Session object"""
2 from sqlalchemy.ext.declarative import declarative_base
2 from sqlalchemy.ext.declarative import declarative_base
3 from sqlalchemy.orm import scoped_session, sessionmaker
3 from sqlalchemy.orm import scoped_session, sessionmaker
4 from beaker import cache
4 from beaker import cache
5
5
6 from rhodecode.lib import caching_query
6 from rhodecode.lib import caching_query
7
7
8
8
9 # Beaker CacheManager. A home base for cache configurations.
9 # Beaker CacheManager. A home base for cache configurations.
10 cache_manager = cache.CacheManager()
10 cache_manager = cache.CacheManager()
11
11
12 __all__ = ['Base', 'Session']
12 __all__ = ['Base', 'Session']
13 #
13 #
14 # SQLAlchemy session manager. Updated by model.init_model()
14 # SQLAlchemy session manager. Updated by model.init_model()
15 #
15 #
16 Session = scoped_session(
16 Session = scoped_session(
17 sessionmaker(
17 sessionmaker(
18 query_cls=caching_query.query_callable(cache_manager)
18 query_cls = caching_query.query_callable(cache_manager),
19 expire_on_commit = True,
19 )
20 )
20 )
21 )
21
22
22 # The declarative Base
23 # The declarative Base
23 Base = declarative_base()
24 Base = declarative_base()
24
25
25 #to use cache use this in query
26 #to use cache use this in query
26 #.options(FromCache("sqlalchemy_cache_type", "cachekey"))
27 #.options(FromCache("sqlalchemy_cache_type", "cachekey"))
@@ -1,91 +1,138 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.notification
3 rhodecode.model.notification
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 Model for notifications
6 Model for notifications
7
7
8
8
9 :created_on: Nov 20, 2011
9 :created_on: Nov 20, 2011
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from rhodecode.lib import safe_unicode
32 from rhodecode.lib.helpers import age
33 from rhodecode.lib.caching_query import FromCache
34
33
35 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
36 from rhodecode.model.db import Notification, User, UserNotification
35 from rhodecode.model.db import Notification, User, UserNotification
37
36
37 log = logging.getLogger(__name__)
38
38
39 class NotificationModel(BaseModel):
39 class NotificationModel(BaseModel):
40
40
41
42 def __get_user(self, user):
43 if isinstance(user, User):
44 return user
45 elif isinstance(user, basestring):
46 return User.get_by_username(username=user)
47 elif isinstance(user, int):
48 return User.get(user)
49 else:
50 raise Exception('Unsupported user must be one of int,'
51 'str or User object')
52
53 def __get_notification(self, notification):
54 if isinstance(notification, Notification):
55 return notification
56 elif isinstance(notification, int):
57 return Notification.get(notification)
58 else:
59 if notification:
60 raise Exception('notification must be int or Instance'
61 ' of Notification got %s' % type(notification))
62
63
41 def create(self, created_by, subject, body, recipients,
64 def create(self, created_by, subject, body, recipients,
42 type_=Notification.TYPE_MESSAGE):
65 type_=Notification.TYPE_MESSAGE):
43 """
66 """
44
67
45 Creates notification of given type
68 Creates notification of given type
46
69
47 :param created_by: int, str or User instance. User who created this
70 :param created_by: int, str or User instance. User who created this
48 notification
71 notification
49 :param subject:
72 :param subject:
50 :param body:
73 :param body:
51 :param recipients: list of int, str or User objects
74 :param recipients: list of int, str or User objects
52 :param type_: type of notification
75 :param type_: type of notification
53 """
76 """
54
77
55 if not getattr(recipients, '__iter__', False):
78 if not getattr(recipients, '__iter__', False):
56 raise Exception('recipients must be a list of iterable')
79 raise Exception('recipients must be a list of iterable')
57
80
58 created_by_obj = created_by
81 created_by_obj = self.__get_user(created_by)
59 if not isinstance(created_by, User):
60 created_by_obj = User.get(created_by)
61
62
82
63 recipients_objs = []
83 recipients_objs = []
64 for u in recipients:
84 for u in recipients:
65 if isinstance(u, User):
85 recipients_objs.append(self.__get_user(u))
66 recipients_objs.append(u)
86 recipients_objs = set(recipients_objs)
67 elif isinstance(u, basestring):
87 return Notification.create(created_by=created_by_obj, subject=subject,
68 recipients_objs.append(User.get_by_username(username=u))
88 body=body, recipients=recipients_objs,
69 elif isinstance(u, int):
70 recipients_objs.append(User.get(u))
71 else:
72 raise Exception('Unsupported recipient must be one of int,'
73 'str or User object')
74
75 Notification.create(created_by=created_by_obj, subject=subject,
76 body = body, recipients = recipients_objs,
77 type_=type_)
89 type_=type_)
78
90
91 def delete(self, notification_id):
92 # we don't want to remove actuall notification just the assignment
93 try:
94 notification_id = int(notification_id)
95 no = self.__get_notification(notification_id)
96 if no:
97 UserNotification.delete(no.notifications_to_users.user_to_notification_id)
98 return True
99 except Exception:
100 log.error(traceback.format_exc())
101 raise
79
102
80 def get_for_user(self, user_id):
103 def get_for_user(self, user_id):
81 return User.get(user_id).notifications
104 return User.get(user_id).notifications
82
105
83 def get_unread_cnt_for_user(self, user_id):
106 def get_unread_cnt_for_user(self, user_id):
84 return UserNotification.query()\
107 return UserNotification.query()\
85 .filter(UserNotification.sent_on == None)\
108 .filter(UserNotification.read == False)\
86 .filter(UserNotification.user_id == user_id).count()
109 .filter(UserNotification.user_id == user_id).count()
87
110
88 def get_unread_for_user(self, user_id):
111 def get_unread_for_user(self, user_id):
89 return [x.notification for x in UserNotification.query()\
112 return [x.notification for x in UserNotification.query()\
90 .filter(UserNotification.sent_on == None)\
113 .filter(UserNotification.read == False)\
91 .filter(UserNotification.user_id == user_id).all()]
114 .filter(UserNotification.user_id == user_id).all()]
115
116 def get_user_notification(self, user, notification):
117 user = self.__get_user(user)
118 notification = self.__get_notification(notification)
119
120 return UserNotification.query()\
121 .filter(UserNotification.notification == notification)\
122 .filter(UserNotification.user == user).scalar()
123
124 def make_description(self, notification):
125 """
126 Creates a human readable description based on properties
127 of notification object
128 """
129
130 _map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'),
131 notification.TYPE_MESSAGE:_('sent message'),
132 notification.TYPE_MENTION:_('mentioned you')}
133
134 tmpl = "%(user)s %(action)s %(when)s"
135 data = dict(user=notification.created_by_user.username,
136 action=_map[notification.type_],
137 when=age(notification.created_on))
138 return tmpl % data
@@ -1,3484 +1,3510 b''
1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td
1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td
2 {
2 {
3 border: 0;
3 border: 0;
4 outline: 0;
4 outline: 0;
5 font-size: 100%;
5 font-size: 100%;
6 vertical-align: baseline;
6 vertical-align: baseline;
7 background: transparent;
7 background: transparent;
8 margin: 0;
8 margin: 0;
9 padding: 0;
9 padding: 0;
10 }
10 }
11
11
12 body {
12 body {
13 line-height: 1;
13 line-height: 1;
14 height: 100%;
14 height: 100%;
15 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
17 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
17 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
18 color: #000;
18 color: #000;
19 margin: 0;
19 margin: 0;
20 padding: 0;
20 padding: 0;
21 font-size: 12px;
21 font-size: 12px;
22 }
22 }
23
23
24 ol,ul {
24 ol,ul {
25 list-style: none;
25 list-style: none;
26 }
26 }
27
27
28 blockquote,q {
28 blockquote,q {
29 quotes: none;
29 quotes: none;
30 }
30 }
31
31
32 blockquote:before,blockquote:after,q:before,q:after {
32 blockquote:before,blockquote:after,q:before,q:after {
33 content: none;
33 content: none;
34 }
34 }
35
35
36 :focus {
36 :focus {
37 outline: 0;
37 outline: 0;
38 }
38 }
39
39
40 del {
40 del {
41 text-decoration: line-through;
41 text-decoration: line-through;
42 }
42 }
43
43
44 table {
44 table {
45 border-collapse: collapse;
45 border-collapse: collapse;
46 border-spacing: 0;
46 border-spacing: 0;
47 }
47 }
48
48
49 html {
49 html {
50 height: 100%;
50 height: 100%;
51 }
51 }
52
52
53 a {
53 a {
54 color: #003367;
54 color: #003367;
55 text-decoration: none;
55 text-decoration: none;
56 cursor: pointer;
56 cursor: pointer;
57 }
57 }
58
58
59 a:hover {
59 a:hover {
60 color: #316293;
60 color: #316293;
61 text-decoration: underline;
61 text-decoration: underline;
62 }
62 }
63
63
64 h1,h2,h3,h4,h5,h6 {
64 h1,h2,h3,h4,h5,h6 {
65 color: #292929;
65 color: #292929;
66 font-weight: 700;
66 font-weight: 700;
67 }
67 }
68
68
69 h1 {
69 h1 {
70 font-size: 22px;
70 font-size: 22px;
71 }
71 }
72
72
73 h2 {
73 h2 {
74 font-size: 20px;
74 font-size: 20px;
75 }
75 }
76
76
77 h3 {
77 h3 {
78 font-size: 18px;
78 font-size: 18px;
79 }
79 }
80
80
81 h4 {
81 h4 {
82 font-size: 16px;
82 font-size: 16px;
83 }
83 }
84
84
85 h5 {
85 h5 {
86 font-size: 14px;
86 font-size: 14px;
87 }
87 }
88
88
89 h6 {
89 h6 {
90 font-size: 11px;
90 font-size: 11px;
91 }
91 }
92
92
93 ul.circle {
93 ul.circle {
94 list-style-type: circle;
94 list-style-type: circle;
95 }
95 }
96
96
97 ul.disc {
97 ul.disc {
98 list-style-type: disc;
98 list-style-type: disc;
99 }
99 }
100
100
101 ul.square {
101 ul.square {
102 list-style-type: square;
102 list-style-type: square;
103 }
103 }
104
104
105 ol.lower-roman {
105 ol.lower-roman {
106 list-style-type: lower-roman;
106 list-style-type: lower-roman;
107 }
107 }
108
108
109 ol.upper-roman {
109 ol.upper-roman {
110 list-style-type: upper-roman;
110 list-style-type: upper-roman;
111 }
111 }
112
112
113 ol.lower-alpha {
113 ol.lower-alpha {
114 list-style-type: lower-alpha;
114 list-style-type: lower-alpha;
115 }
115 }
116
116
117 ol.upper-alpha {
117 ol.upper-alpha {
118 list-style-type: upper-alpha;
118 list-style-type: upper-alpha;
119 }
119 }
120
120
121 ol.decimal {
121 ol.decimal {
122 list-style-type: decimal;
122 list-style-type: decimal;
123 }
123 }
124
124
125 div.color {
125 div.color {
126 clear: both;
126 clear: both;
127 overflow: hidden;
127 overflow: hidden;
128 position: absolute;
128 position: absolute;
129 background: #FFF;
129 background: #FFF;
130 margin: 7px 0 0 60px;
130 margin: 7px 0 0 60px;
131 padding: 1px 1px 1px 0;
131 padding: 1px 1px 1px 0;
132 }
132 }
133
133
134 div.color a {
134 div.color a {
135 width: 15px;
135 width: 15px;
136 height: 15px;
136 height: 15px;
137 display: block;
137 display: block;
138 float: left;
138 float: left;
139 margin: 0 0 0 1px;
139 margin: 0 0 0 1px;
140 padding: 0;
140 padding: 0;
141 }
141 }
142
142
143 div.options {
143 div.options {
144 clear: both;
144 clear: both;
145 overflow: hidden;
145 overflow: hidden;
146 position: absolute;
146 position: absolute;
147 background: #FFF;
147 background: #FFF;
148 margin: 7px 0 0 162px;
148 margin: 7px 0 0 162px;
149 padding: 0;
149 padding: 0;
150 }
150 }
151
151
152 div.options a {
152 div.options a {
153 height: 1%;
153 height: 1%;
154 display: block;
154 display: block;
155 text-decoration: none;
155 text-decoration: none;
156 margin: 0;
156 margin: 0;
157 padding: 3px 8px;
157 padding: 3px 8px;
158 }
158 }
159
159
160 .top-left-rounded-corner {
160 .top-left-rounded-corner {
161 -webkit-border-top-left-radius: 8px;
161 -webkit-border-top-left-radius: 8px;
162 -khtml-border-radius-topleft: 8px;
162 -khtml-border-radius-topleft: 8px;
163 -moz-border-radius-topleft: 8px;
163 -moz-border-radius-topleft: 8px;
164 border-top-left-radius: 8px;
164 border-top-left-radius: 8px;
165 }
165 }
166
166
167 .top-right-rounded-corner {
167 .top-right-rounded-corner {
168 -webkit-border-top-right-radius: 8px;
168 -webkit-border-top-right-radius: 8px;
169 -khtml-border-radius-topright: 8px;
169 -khtml-border-radius-topright: 8px;
170 -moz-border-radius-topright: 8px;
170 -moz-border-radius-topright: 8px;
171 border-top-right-radius: 8px;
171 border-top-right-radius: 8px;
172 }
172 }
173
173
174 .bottom-left-rounded-corner {
174 .bottom-left-rounded-corner {
175 -webkit-border-bottom-left-radius: 8px;
175 -webkit-border-bottom-left-radius: 8px;
176 -khtml-border-radius-bottomleft: 8px;
176 -khtml-border-radius-bottomleft: 8px;
177 -moz-border-radius-bottomleft: 8px;
177 -moz-border-radius-bottomleft: 8px;
178 border-bottom-left-radius: 8px;
178 border-bottom-left-radius: 8px;
179 }
179 }
180
180
181 .bottom-right-rounded-corner {
181 .bottom-right-rounded-corner {
182 -webkit-border-bottom-right-radius: 8px;
182 -webkit-border-bottom-right-radius: 8px;
183 -khtml-border-radius-bottomright: 8px;
183 -khtml-border-radius-bottomright: 8px;
184 -moz-border-radius-bottomright: 8px;
184 -moz-border-radius-bottomright: 8px;
185 border-bottom-right-radius: 8px;
185 border-bottom-right-radius: 8px;
186 }
186 }
187
187
188 #header {
188 #header {
189 margin: 0;
189 margin: 0;
190 padding: 0 10px;
190 padding: 0 10px;
191 }
191 }
192
192
193 #header ul#logged-user {
193 #header ul#logged-user {
194 margin-bottom: 5px !important;
194 margin-bottom: 5px !important;
195 -webkit-border-radius: 0px 0px 8px 8px;
195 -webkit-border-radius: 0px 0px 8px 8px;
196 -khtml-border-radius: 0px 0px 8px 8px;
196 -khtml-border-radius: 0px 0px 8px 8px;
197 -moz-border-radius: 0px 0px 8px 8px;
197 -moz-border-radius: 0px 0px 8px 8px;
198 border-radius: 0px 0px 8px 8px;
198 border-radius: 0px 0px 8px 8px;
199 height: 37px;
199 height: 37px;
200 background-color: #eedc94;
200 background-color: #eedc94;
201 background-repeat: repeat-x;
201 background-repeat: repeat-x;
202 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
202 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
203 to(#eedc94) );
203 to(#eedc94) );
204 background-image: -moz-linear-gradient(top, #003b76, #00376e);
204 background-image: -moz-linear-gradient(top, #003b76, #00376e);
205 background-image: -ms-linear-gradient(top, #003b76, #00376e);
205 background-image: -ms-linear-gradient(top, #003b76, #00376e);
206 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
206 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
207 color-stop(100%, #00376e) );
207 color-stop(100%, #00376e) );
208 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
208 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
209 background-image: -o-linear-gradient(top, #003b76, #00376e) );
209 background-image: -o-linear-gradient(top, #003b76, #00376e) );
210 background-image: linear-gradient(top, #003b76, #00376e);
210 background-image: linear-gradient(top, #003b76, #00376e);
211 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
211 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
212 endColorstr='#00376e', GradientType=0 );
212 endColorstr='#00376e', GradientType=0 );
213 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
213 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
214 }
214 }
215
215
216 #header ul#logged-user li {
216 #header ul#logged-user li {
217 list-style: none;
217 list-style: none;
218 float: left;
218 float: left;
219 margin: 8px 0 0;
219 margin: 8px 0 0;
220 padding: 4px 12px;
220 padding: 4px 12px;
221 border-left: 1px solid #316293;
221 border-left: 1px solid #316293;
222 }
222 }
223
223
224 #header ul#logged-user li.first {
224 #header ul#logged-user li.first {
225 border-left: none;
225 border-left: none;
226 margin: 4px;
226 margin: 4px;
227 }
227 }
228
228
229 #header ul#logged-user li.first div.gravatar {
229 #header ul#logged-user li.first div.gravatar {
230 margin-top: -2px;
230 margin-top: -2px;
231 }
231 }
232
232
233 #header ul#logged-user li.first div.account {
233 #header ul#logged-user li.first div.account {
234 padding-top: 4px;
234 padding-top: 4px;
235 float: left;
235 float: left;
236 }
236 }
237
237
238 #header ul#logged-user li.last {
238 #header ul#logged-user li.last {
239 border-right: none;
239 border-right: none;
240 }
240 }
241
241
242 #header ul#logged-user li a {
242 #header ul#logged-user li a {
243 color: #fff;
243 color: #fff;
244 font-weight: 700;
244 font-weight: 700;
245 text-decoration: none;
245 text-decoration: none;
246 }
246 }
247
247
248 #header ul#logged-user li a:hover {
248 #header ul#logged-user li a:hover {
249 text-decoration: underline;
249 text-decoration: underline;
250 }
250 }
251
251
252 #header ul#logged-user li.highlight a {
252 #header ul#logged-user li.highlight a {
253 color: #fff;
253 color: #fff;
254 }
254 }
255
255
256 #header ul#logged-user li.highlight a:hover {
256 #header ul#logged-user li.highlight a:hover {
257 color: #FFF;
257 color: #FFF;
258 }
258 }
259
259
260 #header #header-inner {
260 #header #header-inner {
261 min-height: 40px;
261 min-height: 40px;
262 clear: both;
262 clear: both;
263 position: relative;
263 position: relative;
264 background-color: #eedc94;
264 background-color: #eedc94;
265 background-repeat: repeat-x;
265 background-repeat: repeat-x;
266 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
266 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
267 to(#eedc94) );
267 to(#eedc94) );
268 background-image: -moz-linear-gradient(top, #003b76, #00376e);
268 background-image: -moz-linear-gradient(top, #003b76, #00376e);
269 background-image: -ms-linear-gradient(top, #003b76, #00376e);
269 background-image: -ms-linear-gradient(top, #003b76, #00376e);
270 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
270 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
271 color-stop(100%, #00376e) );
271 color-stop(100%, #00376e) );
272 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
272 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
273 background-image: -o-linear-gradient(top, #003b76, #00376e) );
273 background-image: -o-linear-gradient(top, #003b76, #00376e) );
274 background-image: linear-gradient(top, #003b76, #00376e);
274 background-image: linear-gradient(top, #003b76, #00376e);
275 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
275 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
276 endColorstr='#00376e', GradientType=0 );
276 endColorstr='#00376e', GradientType=0 );
277 margin: 0;
277 margin: 0;
278 padding: 0;
278 padding: 0;
279 display: block;
279 display: block;
280 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
280 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
281 -webkit-border-radius: 4px 4px 4px 4px;
281 -webkit-border-radius: 4px 4px 4px 4px;
282 -khtml-border-radius: 4px 4px 4px 4px;
282 -khtml-border-radius: 4px 4px 4px 4px;
283 -moz-border-radius: 4px 4px 4px 4px;
283 -moz-border-radius: 4px 4px 4px 4px;
284 border-radius: 4px 4px 4px 4px;
284 border-radius: 4px 4px 4px 4px;
285 }
285 }
286 #header #header-inner.hover{
286 #header #header-inner.hover{
287 position: fixed !important;
287 position: fixed !important;
288 width: 100% !important;
288 width: 100% !important;
289 margin-left: -10px !important;
289 margin-left: -10px !important;
290 z-index: 10000;
290 z-index: 10000;
291 border-radius: 0px 0px 4px 4px;
291 border-radius: 0px 0px 4px 4px;
292 }
292 }
293 #header #header-inner #home a {
293 #header #header-inner #home a {
294 height: 40px;
294 height: 40px;
295 width: 46px;
295 width: 46px;
296 display: block;
296 display: block;
297 background: url("../images/button_home.png");
297 background: url("../images/button_home.png");
298 background-position: 0 0;
298 background-position: 0 0;
299 margin: 0;
299 margin: 0;
300 padding: 0;
300 padding: 0;
301 }
301 }
302
302
303 #header #header-inner #home a:hover {
303 #header #header-inner #home a:hover {
304 background-position: 0 -40px;
304 background-position: 0 -40px;
305 }
305 }
306
306
307 #header #header-inner #logo {
307 #header #header-inner #logo {
308 float: left;
308 float: left;
309 position: absolute;
309 position: absolute;
310 }
310 }
311
311
312 #header #header-inner #logo h1 {
312 #header #header-inner #logo h1 {
313 color: #FFF;
313 color: #FFF;
314 font-size: 18px;
314 font-size: 18px;
315 margin: 10px 0 0 13px;
315 margin: 10px 0 0 13px;
316 padding: 0;
316 padding: 0;
317 }
317 }
318
318
319 #header #header-inner #logo a {
319 #header #header-inner #logo a {
320 color: #fff;
320 color: #fff;
321 text-decoration: none;
321 text-decoration: none;
322 }
322 }
323
323
324 #header #header-inner #logo a:hover {
324 #header #header-inner #logo a:hover {
325 color: #bfe3ff;
325 color: #bfe3ff;
326 }
326 }
327
327
328 #header #header-inner #quick,#header #header-inner #quick ul {
328 #header #header-inner #quick,#header #header-inner #quick ul {
329 position: relative;
329 position: relative;
330 float: right;
330 float: right;
331 list-style-type: none;
331 list-style-type: none;
332 list-style-position: outside;
332 list-style-position: outside;
333 margin: 6px 5px 0 0;
333 margin: 6px 5px 0 0;
334 padding: 0;
334 padding: 0;
335 }
335 }
336
336
337 #header #header-inner #quick li {
337 #header #header-inner #quick li {
338 position: relative;
338 position: relative;
339 float: left;
339 float: left;
340 margin: 0 5px 0 0;
340 margin: 0 5px 0 0;
341 padding: 0;
341 padding: 0;
342 }
342 }
343
343
344 #header #header-inner #quick li a {
344 #header #header-inner #quick li a {
345 top: 0;
345 top: 0;
346 left: 0;
346 left: 0;
347 height: 1%;
347 height: 1%;
348 display: block;
348 display: block;
349 clear: both;
349 clear: both;
350 overflow: hidden;
350 overflow: hidden;
351 color: #FFF;
351 color: #FFF;
352 font-weight: 700;
352 font-weight: 700;
353 text-decoration: none;
353 text-decoration: none;
354 background: #369;
354 background: #369;
355 padding: 0;
355 padding: 0;
356 -webkit-border-radius: 4px 4px 4px 4px;
356 -webkit-border-radius: 4px 4px 4px 4px;
357 -khtml-border-radius: 4px 4px 4px 4px;
357 -khtml-border-radius: 4px 4px 4px 4px;
358 -moz-border-radius: 4px 4px 4px 4px;
358 -moz-border-radius: 4px 4px 4px 4px;
359 border-radius: 4px 4px 4px 4px;
359 border-radius: 4px 4px 4px 4px;
360 }
360 }
361
361
362 #header #header-inner #quick li span.short {
362 #header #header-inner #quick li span.short {
363 padding: 9px 6px 8px 6px;
363 padding: 9px 6px 8px 6px;
364 }
364 }
365
365
366 #header #header-inner #quick li span {
366 #header #header-inner #quick li span {
367 top: 0;
367 top: 0;
368 right: 0;
368 right: 0;
369 height: 1%;
369 height: 1%;
370 display: block;
370 display: block;
371 float: left;
371 float: left;
372 border-left: 1px solid #3f6f9f;
372 border-left: 1px solid #3f6f9f;
373 margin: 0;
373 margin: 0;
374 padding: 10px 12px 8px 10px;
374 padding: 10px 12px 8px 10px;
375 }
375 }
376
376
377 #header #header-inner #quick li span.normal {
377 #header #header-inner #quick li span.normal {
378 border: none;
378 border: none;
379 padding: 10px 12px 8px;
379 padding: 10px 12px 8px;
380 }
380 }
381
381
382 #header #header-inner #quick li span.icon {
382 #header #header-inner #quick li span.icon {
383 top: 0;
383 top: 0;
384 left: 0;
384 left: 0;
385 border-left: none;
385 border-left: none;
386 border-right: 1px solid #2e5c89;
386 border-right: 1px solid #2e5c89;
387 padding: 8px 6px 4px;
387 padding: 8px 6px 4px;
388 }
388 }
389
389
390 #header #header-inner #quick li span.icon_short {
390 #header #header-inner #quick li span.icon_short {
391 top: 0;
391 top: 0;
392 left: 0;
392 left: 0;
393 border-left: none;
393 border-left: none;
394 border-right: 1px solid #2e5c89;
394 border-right: 1px solid #2e5c89;
395 padding: 8px 6px 4px;
395 padding: 8px 6px 4px;
396 }
396 }
397
397
398 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
398 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
399 {
399 {
400 margin: 0px -2px 0px 0px;
400 margin: 0px -2px 0px 0px;
401 }
401 }
402
402
403 #header #header-inner #quick li a:hover {
403 #header #header-inner #quick li a:hover {
404 background: #4e4e4e no-repeat top left;
404 background: #4e4e4e no-repeat top left;
405 }
405 }
406
406
407 #header #header-inner #quick li a:hover span {
407 #header #header-inner #quick li a:hover span {
408 border-left: 1px solid #545454;
408 border-left: 1px solid #545454;
409 }
409 }
410
410
411 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
411 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
412 {
412 {
413 border-left: none;
413 border-left: none;
414 border-right: 1px solid #464646;
414 border-right: 1px solid #464646;
415 }
415 }
416
416
417 #header #header-inner #quick ul {
417 #header #header-inner #quick ul {
418 top: 29px;
418 top: 29px;
419 right: 0;
419 right: 0;
420 min-width: 200px;
420 min-width: 200px;
421 display: none;
421 display: none;
422 position: absolute;
422 position: absolute;
423 background: #FFF;
423 background: #FFF;
424 border: 1px solid #666;
424 border: 1px solid #666;
425 border-top: 1px solid #003367;
425 border-top: 1px solid #003367;
426 z-index: 100;
426 z-index: 100;
427 margin: 0;
427 margin: 0;
428 padding: 0;
428 padding: 0;
429 }
429 }
430
430
431 #header #header-inner #quick ul.repo_switcher {
431 #header #header-inner #quick ul.repo_switcher {
432 max-height: 275px;
432 max-height: 275px;
433 overflow-x: hidden;
433 overflow-x: hidden;
434 overflow-y: auto;
434 overflow-y: auto;
435 }
435 }
436
436
437 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
437 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
438 float: none;
438 float: none;
439 margin: 0;
439 margin: 0;
440 border-bottom: 2px solid #003367;
440 border-bottom: 2px solid #003367;
441 }
441 }
442
442
443 #header #header-inner #quick .repo_switcher_type {
443 #header #header-inner #quick .repo_switcher_type {
444 position: absolute;
444 position: absolute;
445 left: 0;
445 left: 0;
446 top: 9px;
446 top: 9px;
447 }
447 }
448
448
449 #header #header-inner #quick li ul li {
449 #header #header-inner #quick li ul li {
450 border-bottom: 1px solid #ddd;
450 border-bottom: 1px solid #ddd;
451 }
451 }
452
452
453 #header #header-inner #quick li ul li a {
453 #header #header-inner #quick li ul li a {
454 width: 182px;
454 width: 182px;
455 height: auto;
455 height: auto;
456 display: block;
456 display: block;
457 float: left;
457 float: left;
458 background: #FFF;
458 background: #FFF;
459 color: #003367;
459 color: #003367;
460 font-weight: 400;
460 font-weight: 400;
461 margin: 0;
461 margin: 0;
462 padding: 7px 9px;
462 padding: 7px 9px;
463 }
463 }
464
464
465 #header #header-inner #quick li ul li a:hover {
465 #header #header-inner #quick li ul li a:hover {
466 color: #000;
466 color: #000;
467 background: #FFF;
467 background: #FFF;
468 }
468 }
469
469
470 #header #header-inner #quick ul ul {
470 #header #header-inner #quick ul ul {
471 top: auto;
471 top: auto;
472 }
472 }
473
473
474 #header #header-inner #quick li ul ul {
474 #header #header-inner #quick li ul ul {
475 right: 200px;
475 right: 200px;
476 max-height: 275px;
476 max-height: 275px;
477 overflow: auto;
477 overflow: auto;
478 overflow-x: hidden;
478 overflow-x: hidden;
479 white-space: normal;
479 white-space: normal;
480 }
480 }
481
481
482 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
482 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
483 {
483 {
484 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
484 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
485 #FFF;
485 #FFF;
486 width: 167px;
486 width: 167px;
487 margin: 0;
487 margin: 0;
488 padding: 12px 9px 7px 24px;
488 padding: 12px 9px 7px 24px;
489 }
489 }
490
490
491 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
491 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
492 {
492 {
493 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
493 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
494 #FFF;
494 #FFF;
495 min-width: 167px;
495 min-width: 167px;
496 margin: 0;
496 margin: 0;
497 padding: 12px 9px 7px 24px;
497 padding: 12px 9px 7px 24px;
498 }
498 }
499
499
500 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
500 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
501 {
501 {
502 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
502 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
503 9px #FFF;
503 9px #FFF;
504 min-width: 167px;
504 min-width: 167px;
505 margin: 0;
505 margin: 0;
506 padding: 12px 9px 7px 24px;
506 padding: 12px 9px 7px 24px;
507 }
507 }
508
508
509 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
509 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
510 {
510 {
511 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
511 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
512 #FFF;
512 #FFF;
513 min-width: 167px;
513 min-width: 167px;
514 margin: 0 0 0 14px;
514 margin: 0 0 0 14px;
515 padding: 12px 9px 7px 24px;
515 padding: 12px 9px 7px 24px;
516 }
516 }
517
517
518 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
518 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
519 {
519 {
520 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
520 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
521 #FFF;
521 #FFF;
522 min-width: 167px;
522 min-width: 167px;
523 margin: 0 0 0 14px;
523 margin: 0 0 0 14px;
524 padding: 12px 9px 7px 24px;
524 padding: 12px 9px 7px 24px;
525 }
525 }
526
526
527 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
527 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
528 {
528 {
529 background: url("../images/icons/database_edit.png") no-repeat scroll
529 background: url("../images/icons/database_edit.png") no-repeat scroll
530 4px 9px #FFF;
530 4px 9px #FFF;
531 width: 167px;
531 width: 167px;
532 margin: 0;
532 margin: 0;
533 padding: 12px 9px 7px 24px;
533 padding: 12px 9px 7px 24px;
534 }
534 }
535
535
536 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
536 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
537 {
537 {
538 background: url("../images/icons/database_link.png") no-repeat scroll
538 background: url("../images/icons/database_link.png") no-repeat scroll
539 4px 9px #FFF;
539 4px 9px #FFF;
540 width: 167px;
540 width: 167px;
541 margin: 0;
541 margin: 0;
542 padding: 12px 9px 7px 24px;
542 padding: 12px 9px 7px 24px;
543 }
543 }
544
544
545 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
545 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
546 {
546 {
547 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
547 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
548 width: 167px;
548 width: 167px;
549 margin: 0;
549 margin: 0;
550 padding: 12px 9px 7px 24px;
550 padding: 12px 9px 7px 24px;
551 }
551 }
552
552
553 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
553 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
554 {
554 {
555 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
555 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
556 width: 167px;
556 width: 167px;
557 margin: 0;
557 margin: 0;
558 padding: 12px 9px 7px 24px;
558 padding: 12px 9px 7px 24px;
559 }
559 }
560
560
561 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
561 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
562 {
562 {
563 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
563 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
564 width: 167px;
564 width: 167px;
565 margin: 0;
565 margin: 0;
566 padding: 12px 9px 7px 24px;
566 padding: 12px 9px 7px 24px;
567 }
567 }
568
568
569 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
569 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
570 {
570 {
571 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
571 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
572 width: 167px;
572 width: 167px;
573 margin: 0;
573 margin: 0;
574 padding: 12px 9px 7px 24px;
574 padding: 12px 9px 7px 24px;
575 }
575 }
576
576
577 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
577 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
578 {
578 {
579 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
579 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
580 width: 167px;
580 width: 167px;
581 margin: 0;
581 margin: 0;
582 padding: 12px 9px 7px 24px;
582 padding: 12px 9px 7px 24px;
583 }
583 }
584
584
585 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
585 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
586 {
586 {
587 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
587 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
588 9px;
588 9px;
589 width: 167px;
589 width: 167px;
590 margin: 0;
590 margin: 0;
591 padding: 12px 9px 7px 24px;
591 padding: 12px 9px 7px 24px;
592 }
592 }
593
593
594 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
594 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
595 {
595 {
596 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
596 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
597 width: 167px;
597 width: 167px;
598 margin: 0;
598 margin: 0;
599 padding: 12px 9px 7px 24px;
599 padding: 12px 9px 7px 24px;
600 }
600 }
601
601
602 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
602 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
603 {
603 {
604 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
604 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
605 width: 167px;
605 width: 167px;
606 margin: 0;
606 margin: 0;
607 padding: 12px 9px 7px 24px;
607 padding: 12px 9px 7px 24px;
608 }
608 }
609
609
610 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
610 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
611 {
611 {
612 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
612 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
613 9px;
613 9px;
614 width: 167px;
614 width: 167px;
615 margin: 0;
615 margin: 0;
616 padding: 12px 9px 7px 24px;
616 padding: 12px 9px 7px 24px;
617 }
617 }
618
618
619 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover
619 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover
620 {
620 {
621 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
621 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
622 width: 167px;
622 width: 167px;
623 margin: 0;
623 margin: 0;
624 padding: 12px 9px 7px 24px;
624 padding: 12px 9px 7px 24px;
625 }
625 }
626
626
627 #header #header-inner #quick li ul li a.admin,#header #header-inner #quick li ul li a.admin:hover
627 #header #header-inner #quick li ul li a.admin,#header #header-inner #quick li ul li a.admin:hover
628 {
628 {
629 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
629 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
630 width: 167px;
630 width: 167px;
631 margin: 0;
631 margin: 0;
632 padding: 12px 9px 7px 24px;
632 padding: 12px 9px 7px 24px;
633 }
633 }
634
634
635 .groups_breadcrumbs a {
635 .groups_breadcrumbs a {
636 color: #fff;
636 color: #fff;
637 }
637 }
638
638
639 .groups_breadcrumbs a:hover {
639 .groups_breadcrumbs a:hover {
640 color: #bfe3ff;
640 color: #bfe3ff;
641 text-decoration: none;
641 text-decoration: none;
642 }
642 }
643
643
644 .quick_repo_menu {
644 .quick_repo_menu {
645 background: #FFF url("../images/vertical-indicator.png") 8px 50%
645 background: #FFF url("../images/vertical-indicator.png") 8px 50%
646 no-repeat !important;
646 no-repeat !important;
647 cursor: pointer;
647 cursor: pointer;
648 width: 8px;
648 width: 8px;
649 }
649 }
650
650
651 .quick_repo_menu.active {
651 .quick_repo_menu.active {
652 background: #FFF url("../images/horizontal-indicator.png") 4px 50%
652 background: #FFF url("../images/horizontal-indicator.png") 4px 50%
653 no-repeat !important;
653 no-repeat !important;
654 cursor: pointer;
654 cursor: pointer;
655 }
655 }
656
656
657 .quick_repo_menu .menu_items {
657 .quick_repo_menu .menu_items {
658 margin-top: 6px;
658 margin-top: 6px;
659 width: 150px;
659 width: 150px;
660 position: absolute;
660 position: absolute;
661 background-color: #FFF;
661 background-color: #FFF;
662 background: none repeat scroll 0 0 #FFFFFF;
662 background: none repeat scroll 0 0 #FFFFFF;
663 border-color: #003367 #666666 #666666;
663 border-color: #003367 #666666 #666666;
664 border-right: 1px solid #666666;
664 border-right: 1px solid #666666;
665 border-style: solid;
665 border-style: solid;
666 border-width: 1px;
666 border-width: 1px;
667 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
667 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
668 }
668 }
669
669
670 .quick_repo_menu .menu_items li {
670 .quick_repo_menu .menu_items li {
671 padding: 0 !important;
671 padding: 0 !important;
672 }
672 }
673
673
674 .quick_repo_menu .menu_items a {
674 .quick_repo_menu .menu_items a {
675 display: block;
675 display: block;
676 padding: 4px 12px 4px 8px;
676 padding: 4px 12px 4px 8px;
677 }
677 }
678
678
679 .quick_repo_menu .menu_items a:hover {
679 .quick_repo_menu .menu_items a:hover {
680 background-color: #EEE;
680 background-color: #EEE;
681 text-decoration: none;
681 text-decoration: none;
682 }
682 }
683
683
684 .quick_repo_menu .menu_items .icon img {
684 .quick_repo_menu .menu_items .icon img {
685 margin-bottom: -2px;
685 margin-bottom: -2px;
686 }
686 }
687
687
688 .quick_repo_menu .menu_items.hidden {
688 .quick_repo_menu .menu_items.hidden {
689 display: none;
689 display: none;
690 }
690 }
691
691
692 #content #left {
692 #content #left {
693 left: 0;
693 left: 0;
694 width: 280px;
694 width: 280px;
695 position: absolute;
695 position: absolute;
696 }
696 }
697
697
698 #content #right {
698 #content #right {
699 margin: 0 60px 10px 290px;
699 margin: 0 60px 10px 290px;
700 }
700 }
701
701
702 #content div.box {
702 #content div.box {
703 clear: both;
703 clear: both;
704 overflow: hidden;
704 overflow: hidden;
705 background: #fff;
705 background: #fff;
706 margin: 0 0 10px;
706 margin: 0 0 10px;
707 padding: 0 0 10px;
707 padding: 0 0 10px;
708 -webkit-border-radius: 4px 4px 4px 4px;
708 -webkit-border-radius: 4px 4px 4px 4px;
709 -khtml-border-radius: 4px 4px 4px 4px;
709 -khtml-border-radius: 4px 4px 4px 4px;
710 -moz-border-radius: 4px 4px 4px 4px;
710 -moz-border-radius: 4px 4px 4px 4px;
711 border-radius: 4px 4px 4px 4px;
711 border-radius: 4px 4px 4px 4px;
712 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
712 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
713 }
713 }
714
714
715 #content div.box-left {
715 #content div.box-left {
716 width: 49%;
716 width: 49%;
717 clear: none;
717 clear: none;
718 float: left;
718 float: left;
719 margin: 0 0 10px;
719 margin: 0 0 10px;
720 }
720 }
721
721
722 #content div.box-right {
722 #content div.box-right {
723 width: 49%;
723 width: 49%;
724 clear: none;
724 clear: none;
725 float: right;
725 float: right;
726 margin: 0 0 10px;
726 margin: 0 0 10px;
727 }
727 }
728
728
729 #content div.box div.title {
729 #content div.box div.title {
730 clear: both;
730 clear: both;
731 overflow: hidden;
731 overflow: hidden;
732 background-color: #eedc94;
732 background-color: #eedc94;
733 background-repeat: repeat-x;
733 background-repeat: repeat-x;
734 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
734 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
735 to(#eedc94) );
735 to(#eedc94) );
736 background-image: -moz-linear-gradient(top, #003b76, #00376e);
736 background-image: -moz-linear-gradient(top, #003b76, #00376e);
737 background-image: -ms-linear-gradient(top, #003b76, #00376e);
737 background-image: -ms-linear-gradient(top, #003b76, #00376e);
738 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
738 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
739 color-stop(100%, #00376e) );
739 color-stop(100%, #00376e) );
740 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
740 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
741 background-image: -o-linear-gradient(top, #003b76, #00376e) );
741 background-image: -o-linear-gradient(top, #003b76, #00376e) );
742 background-image: linear-gradient(top, #003b76, #00376e);
742 background-image: linear-gradient(top, #003b76, #00376e);
743 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
743 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
744 endColorstr='#00376e', GradientType=0 );
744 endColorstr='#00376e', GradientType=0 );
745 margin: 0 0 20px;
745 margin: 0 0 20px;
746 padding: 0;
746 padding: 0;
747 }
747 }
748
748
749 #content div.box div.title h5 {
749 #content div.box div.title h5 {
750 float: left;
750 float: left;
751 border: none;
751 border: none;
752 color: #fff;
752 color: #fff;
753 text-transform: uppercase;
753 text-transform: uppercase;
754 margin: 0;
754 margin: 0;
755 padding: 11px 0 11px 10px;
755 padding: 11px 0 11px 10px;
756 }
756 }
757
757
758 #content div.box div.title ul.links li {
758 #content div.box div.title ul.links li {
759 list-style: none;
759 list-style: none;
760 float: left;
760 float: left;
761 margin: 0;
761 margin: 0;
762 padding: 0;
762 padding: 0;
763 }
763 }
764
764
765 #content div.box div.title ul.links li a {
765 #content div.box div.title ul.links li a {
766 border-left: 1px solid #316293;
766 border-left: 1px solid #316293;
767 color: #FFFFFF;
767 color: #FFFFFF;
768 display: block;
768 display: block;
769 float: left;
769 float: left;
770 font-size: 13px;
770 font-size: 13px;
771 font-weight: 700;
771 font-weight: 700;
772 height: 1%;
772 height: 1%;
773 margin: 0;
773 margin: 0;
774 padding: 11px 22px 12px;
774 padding: 11px 22px 12px;
775 text-decoration: none;
775 text-decoration: none;
776 }
776 }
777
777
778 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6
778 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6
779 {
779 {
780 clear: both;
780 clear: both;
781 overflow: hidden;
781 overflow: hidden;
782 border-bottom: 1px solid #DDD;
782 border-bottom: 1px solid #DDD;
783 margin: 10px 20px;
783 margin: 10px 20px;
784 padding: 0 0 15px;
784 padding: 0 0 15px;
785 }
785 }
786
786
787 #content div.box p {
787 #content div.box p {
788 color: #5f5f5f;
788 color: #5f5f5f;
789 font-size: 12px;
789 font-size: 12px;
790 line-height: 150%;
790 line-height: 150%;
791 margin: 0 24px 10px;
791 margin: 0 24px 10px;
792 padding: 0;
792 padding: 0;
793 }
793 }
794
794
795 #content div.box blockquote {
795 #content div.box blockquote {
796 border-left: 4px solid #DDD;
796 border-left: 4px solid #DDD;
797 color: #5f5f5f;
797 color: #5f5f5f;
798 font-size: 11px;
798 font-size: 11px;
799 line-height: 150%;
799 line-height: 150%;
800 margin: 0 34px;
800 margin: 0 34px;
801 padding: 0 0 0 14px;
801 padding: 0 0 0 14px;
802 }
802 }
803
803
804 #content div.box blockquote p {
804 #content div.box blockquote p {
805 margin: 10px 0;
805 margin: 10px 0;
806 padding: 0;
806 padding: 0;
807 }
807 }
808
808
809 #content div.box dl {
809 #content div.box dl {
810 margin: 10px 24px;
810 margin: 10px 24px;
811 }
811 }
812
812
813 #content div.box dt {
813 #content div.box dt {
814 font-size: 12px;
814 font-size: 12px;
815 margin: 0;
815 margin: 0;
816 }
816 }
817
817
818 #content div.box dd {
818 #content div.box dd {
819 font-size: 12px;
819 font-size: 12px;
820 margin: 0;
820 margin: 0;
821 padding: 8px 0 8px 15px;
821 padding: 8px 0 8px 15px;
822 }
822 }
823
823
824 #content div.box li {
824 #content div.box li {
825 font-size: 12px;
825 font-size: 12px;
826 padding: 4px 0;
826 padding: 4px 0;
827 }
827 }
828
828
829 #content div.box ul.disc,#content div.box ul.circle {
829 #content div.box ul.disc,#content div.box ul.circle {
830 margin: 10px 24px 10px 38px;
830 margin: 10px 24px 10px 38px;
831 }
831 }
832
832
833 #content div.box ul.square {
833 #content div.box ul.square {
834 margin: 10px 24px 10px 40px;
834 margin: 10px 24px 10px 40px;
835 }
835 }
836
836
837 #content div.box img.left {
837 #content div.box img.left {
838 border: none;
838 border: none;
839 float: left;
839 float: left;
840 margin: 10px 10px 10px 0;
840 margin: 10px 10px 10px 0;
841 }
841 }
842
842
843 #content div.box img.right {
843 #content div.box img.right {
844 border: none;
844 border: none;
845 float: right;
845 float: right;
846 margin: 10px 0 10px 10px;
846 margin: 10px 0 10px 10px;
847 }
847 }
848
848
849 #content div.box div.messages {
849 #content div.box div.messages {
850 clear: both;
850 clear: both;
851 overflow: hidden;
851 overflow: hidden;
852 margin: 0 20px;
852 margin: 0 20px;
853 padding: 0;
853 padding: 0;
854 }
854 }
855
855
856 #content div.box div.message {
856 #content div.box div.message {
857 clear: both;
857 clear: both;
858 overflow: hidden;
858 overflow: hidden;
859 margin: 0;
859 margin: 0;
860 padding: 10px 0;
860 padding: 10px 0;
861 }
861 }
862
862
863 #content div.box div.message a {
863 #content div.box div.message a {
864 font-weight: 400 !important;
864 font-weight: 400 !important;
865 }
865 }
866
866
867 #content div.box div.message div.image {
867 #content div.box div.message div.image {
868 float: left;
868 float: left;
869 margin: 9px 0 0 5px;
869 margin: 9px 0 0 5px;
870 padding: 6px;
870 padding: 6px;
871 }
871 }
872
872
873 #content div.box div.message div.image img {
873 #content div.box div.message div.image img {
874 vertical-align: middle;
874 vertical-align: middle;
875 margin: 0;
875 margin: 0;
876 }
876 }
877
877
878 #content div.box div.message div.text {
878 #content div.box div.message div.text {
879 float: left;
879 float: left;
880 margin: 0;
880 margin: 0;
881 padding: 9px 6px;
881 padding: 9px 6px;
882 }
882 }
883
883
884 #content div.box div.message div.dismiss a {
884 #content div.box div.message div.dismiss a {
885 height: 16px;
885 height: 16px;
886 width: 16px;
886 width: 16px;
887 display: block;
887 display: block;
888 background: url("../images/icons/cross.png") no-repeat;
888 background: url("../images/icons/cross.png") no-repeat;
889 margin: 15px 14px 0 0;
889 margin: 15px 14px 0 0;
890 padding: 0;
890 padding: 0;
891 }
891 }
892
892
893 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6
893 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6
894 {
894 {
895 border: none;
895 border: none;
896 margin: 0;
896 margin: 0;
897 padding: 0;
897 padding: 0;
898 }
898 }
899
899
900 #content div.box div.message div.text span {
900 #content div.box div.message div.text span {
901 height: 1%;
901 height: 1%;
902 display: block;
902 display: block;
903 margin: 0;
903 margin: 0;
904 padding: 5px 0 0;
904 padding: 5px 0 0;
905 }
905 }
906
906
907 #content div.box div.message-error {
907 #content div.box div.message-error {
908 height: 1%;
908 height: 1%;
909 clear: both;
909 clear: both;
910 overflow: hidden;
910 overflow: hidden;
911 background: #FBE3E4;
911 background: #FBE3E4;
912 border: 1px solid #FBC2C4;
912 border: 1px solid #FBC2C4;
913 color: #860006;
913 color: #860006;
914 }
914 }
915
915
916 #content div.box div.message-error h6 {
916 #content div.box div.message-error h6 {
917 color: #860006;
917 color: #860006;
918 }
918 }
919
919
920 #content div.box div.message-warning {
920 #content div.box div.message-warning {
921 height: 1%;
921 height: 1%;
922 clear: both;
922 clear: both;
923 overflow: hidden;
923 overflow: hidden;
924 background: #FFF6BF;
924 background: #FFF6BF;
925 border: 1px solid #FFD324;
925 border: 1px solid #FFD324;
926 color: #5f5200;
926 color: #5f5200;
927 }
927 }
928
928
929 #content div.box div.message-warning h6 {
929 #content div.box div.message-warning h6 {
930 color: #5f5200;
930 color: #5f5200;
931 }
931 }
932
932
933 #content div.box div.message-notice {
933 #content div.box div.message-notice {
934 height: 1%;
934 height: 1%;
935 clear: both;
935 clear: both;
936 overflow: hidden;
936 overflow: hidden;
937 background: #8FBDE0;
937 background: #8FBDE0;
938 border: 1px solid #6BACDE;
938 border: 1px solid #6BACDE;
939 color: #003863;
939 color: #003863;
940 }
940 }
941
941
942 #content div.box div.message-notice h6 {
942 #content div.box div.message-notice h6 {
943 color: #003863;
943 color: #003863;
944 }
944 }
945
945
946 #content div.box div.message-success {
946 #content div.box div.message-success {
947 height: 1%;
947 height: 1%;
948 clear: both;
948 clear: both;
949 overflow: hidden;
949 overflow: hidden;
950 background: #E6EFC2;
950 background: #E6EFC2;
951 border: 1px solid #C6D880;
951 border: 1px solid #C6D880;
952 color: #4e6100;
952 color: #4e6100;
953 }
953 }
954
954
955 #content div.box div.message-success h6 {
955 #content div.box div.message-success h6 {
956 color: #4e6100;
956 color: #4e6100;
957 }
957 }
958
958
959 #content div.box div.form div.fields div.field {
959 #content div.box div.form div.fields div.field {
960 height: 1%;
960 height: 1%;
961 border-bottom: 1px solid #DDD;
961 border-bottom: 1px solid #DDD;
962 clear: both;
962 clear: both;
963 margin: 0;
963 margin: 0;
964 padding: 10px 0;
964 padding: 10px 0;
965 }
965 }
966
966
967 #content div.box div.form div.fields div.field-first {
967 #content div.box div.form div.fields div.field-first {
968 padding: 0 0 10px;
968 padding: 0 0 10px;
969 }
969 }
970
970
971 #content div.box div.form div.fields div.field-noborder {
971 #content div.box div.form div.fields div.field-noborder {
972 border-bottom: 0 !important;
972 border-bottom: 0 !important;
973 }
973 }
974
974
975 #content div.box div.form div.fields div.field span.error-message {
975 #content div.box div.form div.fields div.field span.error-message {
976 height: 1%;
976 height: 1%;
977 display: inline-block;
977 display: inline-block;
978 color: red;
978 color: red;
979 margin: 8px 0 0 4px;
979 margin: 8px 0 0 4px;
980 padding: 0;
980 padding: 0;
981 }
981 }
982
982
983 #content div.box div.form div.fields div.field span.success {
983 #content div.box div.form div.fields div.field span.success {
984 height: 1%;
984 height: 1%;
985 display: block;
985 display: block;
986 color: #316309;
986 color: #316309;
987 margin: 8px 0 0;
987 margin: 8px 0 0;
988 padding: 0;
988 padding: 0;
989 }
989 }
990
990
991 #content div.box div.form div.fields div.field div.label {
991 #content div.box div.form div.fields div.field div.label {
992 left: 70px;
992 left: 70px;
993 width: 155px;
993 width: 155px;
994 position: absolute;
994 position: absolute;
995 margin: 0;
995 margin: 0;
996 padding: 8px 0 0 5px;
996 padding: 8px 0 0 5px;
997 }
997 }
998
998
999 #content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label
999 #content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label
1000 {
1000 {
1001 clear: both;
1001 clear: both;
1002 overflow: hidden;
1002 overflow: hidden;
1003 left: 0;
1003 left: 0;
1004 width: auto;
1004 width: auto;
1005 position: relative;
1005 position: relative;
1006 margin: 0;
1006 margin: 0;
1007 padding: 0 0 8px;
1007 padding: 0 0 8px;
1008 }
1008 }
1009
1009
1010 #content div.box div.form div.fields div.field div.label-select {
1010 #content div.box div.form div.fields div.field div.label-select {
1011 padding: 5px 0 0 5px;
1011 padding: 5px 0 0 5px;
1012 }
1012 }
1013
1013
1014 #content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select
1014 #content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select
1015 {
1015 {
1016 padding: 0 0 8px;
1016 padding: 0 0 8px;
1017 }
1017 }
1018
1018
1019 #content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea
1019 #content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea
1020 {
1020 {
1021 padding: 0 0 8px !important;
1021 padding: 0 0 8px !important;
1022 }
1022 }
1023
1023
1024 #content div.box div.form div.fields div.field div.label label,div.label label
1024 #content div.box div.form div.fields div.field div.label label,div.label label
1025 {
1025 {
1026 color: #393939;
1026 color: #393939;
1027 font-weight: 700;
1027 font-weight: 700;
1028 }
1028 }
1029
1029
1030 #content div.box div.form div.fields div.field div.input {
1030 #content div.box div.form div.fields div.field div.input {
1031 margin: 0 0 0 200px;
1031 margin: 0 0 0 200px;
1032 }
1032 }
1033
1033
1034 #content div.box div.form div.fields div.field div.file {
1034 #content div.box div.form div.fields div.field div.file {
1035 margin: 0 0 0 200px;
1035 margin: 0 0 0 200px;
1036 }
1036 }
1037
1037
1038 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1038 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1039 {
1039 {
1040 margin: 0 0 0 0px;
1040 margin: 0 0 0 0px;
1041 }
1041 }
1042
1042
1043 #content div.box div.form div.fields div.field div.input input {
1043 #content div.box div.form div.fields div.field div.input input {
1044 background: #FFF;
1044 background: #FFF;
1045 border-top: 1px solid #b3b3b3;
1045 border-top: 1px solid #b3b3b3;
1046 border-left: 1px solid #b3b3b3;
1046 border-left: 1px solid #b3b3b3;
1047 border-right: 1px solid #eaeaea;
1047 border-right: 1px solid #eaeaea;
1048 border-bottom: 1px solid #eaeaea;
1048 border-bottom: 1px solid #eaeaea;
1049 color: #000;
1049 color: #000;
1050 font-size: 11px;
1050 font-size: 11px;
1051 margin: 0;
1051 margin: 0;
1052 padding: 7px 7px 6px;
1052 padding: 7px 7px 6px;
1053 }
1053 }
1054
1054
1055 #content div.box div.form div.fields div.field div.file input {
1055 #content div.box div.form div.fields div.field div.file input {
1056 background: none repeat scroll 0 0 #FFFFFF;
1056 background: none repeat scroll 0 0 #FFFFFF;
1057 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1057 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1058 border-style: solid;
1058 border-style: solid;
1059 border-width: 1px;
1059 border-width: 1px;
1060 color: #000000;
1060 color: #000000;
1061 font-size: 11px;
1061 font-size: 11px;
1062 margin: 0;
1062 margin: 0;
1063 padding: 7px 7px 6px;
1063 padding: 7px 7px 6px;
1064 }
1064 }
1065
1065
1066 #content div.box div.form div.fields div.field div.input input.small {
1066 #content div.box div.form div.fields div.field div.input input.small {
1067 width: 30%;
1067 width: 30%;
1068 }
1068 }
1069
1069
1070 #content div.box div.form div.fields div.field div.input input.medium {
1070 #content div.box div.form div.fields div.field div.input input.medium {
1071 width: 55%;
1071 width: 55%;
1072 }
1072 }
1073
1073
1074 #content div.box div.form div.fields div.field div.input input.large {
1074 #content div.box div.form div.fields div.field div.input input.large {
1075 width: 85%;
1075 width: 85%;
1076 }
1076 }
1077
1077
1078 #content div.box div.form div.fields div.field div.input input.date {
1078 #content div.box div.form div.fields div.field div.input input.date {
1079 width: 177px;
1079 width: 177px;
1080 }
1080 }
1081
1081
1082 #content div.box div.form div.fields div.field div.input input.button {
1082 #content div.box div.form div.fields div.field div.input input.button {
1083 background: #D4D0C8;
1083 background: #D4D0C8;
1084 border-top: 1px solid #FFF;
1084 border-top: 1px solid #FFF;
1085 border-left: 1px solid #FFF;
1085 border-left: 1px solid #FFF;
1086 border-right: 1px solid #404040;
1086 border-right: 1px solid #404040;
1087 border-bottom: 1px solid #404040;
1087 border-bottom: 1px solid #404040;
1088 color: #000;
1088 color: #000;
1089 margin: 0;
1089 margin: 0;
1090 padding: 4px 8px;
1090 padding: 4px 8px;
1091 }
1091 }
1092
1092
1093 #content div.box div.form div.fields div.field div.textarea {
1093 #content div.box div.form div.fields div.field div.textarea {
1094 border-top: 1px solid #b3b3b3;
1094 border-top: 1px solid #b3b3b3;
1095 border-left: 1px solid #b3b3b3;
1095 border-left: 1px solid #b3b3b3;
1096 border-right: 1px solid #eaeaea;
1096 border-right: 1px solid #eaeaea;
1097 border-bottom: 1px solid #eaeaea;
1097 border-bottom: 1px solid #eaeaea;
1098 margin: 0 0 0 200px;
1098 margin: 0 0 0 200px;
1099 padding: 10px;
1099 padding: 10px;
1100 }
1100 }
1101
1101
1102 #content div.box div.form div.fields div.field div.textarea-editor {
1102 #content div.box div.form div.fields div.field div.textarea-editor {
1103 border: 1px solid #ddd;
1103 border: 1px solid #ddd;
1104 padding: 0;
1104 padding: 0;
1105 }
1105 }
1106
1106
1107 #content div.box div.form div.fields div.field div.textarea textarea {
1107 #content div.box div.form div.fields div.field div.textarea textarea {
1108 width: 100%;
1108 width: 100%;
1109 height: 220px;
1109 height: 220px;
1110 overflow: hidden;
1110 overflow: hidden;
1111 background: #FFF;
1111 background: #FFF;
1112 color: #000;
1112 color: #000;
1113 font-size: 11px;
1113 font-size: 11px;
1114 outline: none;
1114 outline: none;
1115 border-width: 0;
1115 border-width: 0;
1116 margin: 0;
1116 margin: 0;
1117 padding: 0;
1117 padding: 0;
1118 }
1118 }
1119
1119
1120 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea
1120 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea
1121 {
1121 {
1122 width: 100%;
1122 width: 100%;
1123 height: 100px;
1123 height: 100px;
1124 }
1124 }
1125
1125
1126 #content div.box div.form div.fields div.field div.textarea table {
1126 #content div.box div.form div.fields div.field div.textarea table {
1127 width: 100%;
1127 width: 100%;
1128 border: none;
1128 border: none;
1129 margin: 0;
1129 margin: 0;
1130 padding: 0;
1130 padding: 0;
1131 }
1131 }
1132
1132
1133 #content div.box div.form div.fields div.field div.textarea table td {
1133 #content div.box div.form div.fields div.field div.textarea table td {
1134 background: #DDD;
1134 background: #DDD;
1135 border: none;
1135 border: none;
1136 padding: 0;
1136 padding: 0;
1137 }
1137 }
1138
1138
1139 #content div.box div.form div.fields div.field div.textarea table td table
1139 #content div.box div.form div.fields div.field div.textarea table td table
1140 {
1140 {
1141 width: auto;
1141 width: auto;
1142 border: none;
1142 border: none;
1143 margin: 0;
1143 margin: 0;
1144 padding: 0;
1144 padding: 0;
1145 }
1145 }
1146
1146
1147 #content div.box div.form div.fields div.field div.textarea table td table td
1147 #content div.box div.form div.fields div.field div.textarea table td table td
1148 {
1148 {
1149 font-size: 11px;
1149 font-size: 11px;
1150 padding: 5px 5px 5px 0;
1150 padding: 5px 5px 5px 0;
1151 }
1151 }
1152
1152
1153 #content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus
1153 #content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus
1154 {
1154 {
1155 background: #f6f6f6;
1155 background: #f6f6f6;
1156 border-color: #666;
1156 border-color: #666;
1157 }
1157 }
1158
1158
1159 div.form div.fields div.field div.button {
1159 div.form div.fields div.field div.button {
1160 margin: 0;
1160 margin: 0;
1161 padding: 0 0 0 8px;
1161 padding: 0 0 0 8px;
1162 }
1162 }
1163 #content div.box table.noborder {
1163 #content div.box table.noborder {
1164 border: 1px solid transparent;
1164 border: 1px solid transparent;
1165 }
1165 }
1166
1166
1167 #content div.box table {
1167 #content div.box table {
1168 width: 100%;
1168 width: 100%;
1169 border-collapse: separate;
1169 border-collapse: separate;
1170 margin: 0;
1170 margin: 0;
1171 padding: 0;
1171 padding: 0;
1172 border: 1px solid #eee;
1172 border: 1px solid #eee;
1173 -webkit-border-radius: 4px;
1173 -webkit-border-radius: 4px;
1174 -moz-border-radius: 4px;
1174 -moz-border-radius: 4px;
1175 border-radius: 4px;
1175 border-radius: 4px;
1176 }
1176 }
1177
1177
1178 #content div.box table th {
1178 #content div.box table th {
1179 background: #eee;
1179 background: #eee;
1180 border-bottom: 1px solid #ddd;
1180 border-bottom: 1px solid #ddd;
1181 padding: 5px 0px 5px 5px;
1181 padding: 5px 0px 5px 5px;
1182 }
1182 }
1183
1183
1184 #content div.box table th.left {
1184 #content div.box table th.left {
1185 text-align: left;
1185 text-align: left;
1186 }
1186 }
1187
1187
1188 #content div.box table th.right {
1188 #content div.box table th.right {
1189 text-align: right;
1189 text-align: right;
1190 }
1190 }
1191
1191
1192 #content div.box table th.center {
1192 #content div.box table th.center {
1193 text-align: center;
1193 text-align: center;
1194 }
1194 }
1195
1195
1196 #content div.box table th.selected {
1196 #content div.box table th.selected {
1197 vertical-align: middle;
1197 vertical-align: middle;
1198 padding: 0;
1198 padding: 0;
1199 }
1199 }
1200
1200
1201 #content div.box table td {
1201 #content div.box table td {
1202 background: #fff;
1202 background: #fff;
1203 border-bottom: 1px solid #cdcdcd;
1203 border-bottom: 1px solid #cdcdcd;
1204 vertical-align: middle;
1204 vertical-align: middle;
1205 padding: 5px;
1205 padding: 5px;
1206 }
1206 }
1207
1207
1208 #content div.box table tr.selected td {
1208 #content div.box table tr.selected td {
1209 background: #FFC;
1209 background: #FFC;
1210 }
1210 }
1211
1211
1212 #content div.box table td.selected {
1212 #content div.box table td.selected {
1213 width: 3%;
1213 width: 3%;
1214 text-align: center;
1214 text-align: center;
1215 vertical-align: middle;
1215 vertical-align: middle;
1216 padding: 0;
1216 padding: 0;
1217 }
1217 }
1218
1218
1219 #content div.box table td.action {
1219 #content div.box table td.action {
1220 width: 45%;
1220 width: 45%;
1221 text-align: left;
1221 text-align: left;
1222 }
1222 }
1223
1223
1224 #content div.box table td.date {
1224 #content div.box table td.date {
1225 width: 33%;
1225 width: 33%;
1226 text-align: center;
1226 text-align: center;
1227 }
1227 }
1228
1228
1229 #content div.box div.action {
1229 #content div.box div.action {
1230 float: right;
1230 float: right;
1231 background: #FFF;
1231 background: #FFF;
1232 text-align: right;
1232 text-align: right;
1233 margin: 10px 0 0;
1233 margin: 10px 0 0;
1234 padding: 0;
1234 padding: 0;
1235 }
1235 }
1236
1236
1237 #content div.box div.action select {
1237 #content div.box div.action select {
1238 font-size: 11px;
1238 font-size: 11px;
1239 margin: 0;
1239 margin: 0;
1240 }
1240 }
1241
1241
1242 #content div.box div.action .ui-selectmenu {
1242 #content div.box div.action .ui-selectmenu {
1243 margin: 0;
1243 margin: 0;
1244 padding: 0;
1244 padding: 0;
1245 }
1245 }
1246
1246
1247 #content div.box div.pagination {
1247 #content div.box div.pagination {
1248 height: 1%;
1248 height: 1%;
1249 clear: both;
1249 clear: both;
1250 overflow: hidden;
1250 overflow: hidden;
1251 margin: 10px 0 0;
1251 margin: 10px 0 0;
1252 padding: 0;
1252 padding: 0;
1253 }
1253 }
1254
1254
1255 #content div.box div.pagination ul.pager {
1255 #content div.box div.pagination ul.pager {
1256 float: right;
1256 float: right;
1257 text-align: right;
1257 text-align: right;
1258 margin: 0;
1258 margin: 0;
1259 padding: 0;
1259 padding: 0;
1260 }
1260 }
1261
1261
1262 #content div.box div.pagination ul.pager li {
1262 #content div.box div.pagination ul.pager li {
1263 height: 1%;
1263 height: 1%;
1264 float: left;
1264 float: left;
1265 list-style: none;
1265 list-style: none;
1266 background: #ebebeb url("../images/pager.png") repeat-x;
1266 background: #ebebeb url("../images/pager.png") repeat-x;
1267 border-top: 1px solid #dedede;
1267 border-top: 1px solid #dedede;
1268 border-left: 1px solid #cfcfcf;
1268 border-left: 1px solid #cfcfcf;
1269 border-right: 1px solid #c4c4c4;
1269 border-right: 1px solid #c4c4c4;
1270 border-bottom: 1px solid #c4c4c4;
1270 border-bottom: 1px solid #c4c4c4;
1271 color: #4A4A4A;
1271 color: #4A4A4A;
1272 font-weight: 700;
1272 font-weight: 700;
1273 margin: 0 0 0 4px;
1273 margin: 0 0 0 4px;
1274 padding: 0;
1274 padding: 0;
1275 }
1275 }
1276
1276
1277 #content div.box div.pagination ul.pager li.separator {
1277 #content div.box div.pagination ul.pager li.separator {
1278 padding: 6px;
1278 padding: 6px;
1279 }
1279 }
1280
1280
1281 #content div.box div.pagination ul.pager li.current {
1281 #content div.box div.pagination ul.pager li.current {
1282 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1282 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1283 border-top: 1px solid #ccc;
1283 border-top: 1px solid #ccc;
1284 border-left: 1px solid #bebebe;
1284 border-left: 1px solid #bebebe;
1285 border-right: 1px solid #b1b1b1;
1285 border-right: 1px solid #b1b1b1;
1286 border-bottom: 1px solid #afafaf;
1286 border-bottom: 1px solid #afafaf;
1287 color: #515151;
1287 color: #515151;
1288 padding: 6px;
1288 padding: 6px;
1289 }
1289 }
1290
1290
1291 #content div.box div.pagination ul.pager li a {
1291 #content div.box div.pagination ul.pager li a {
1292 height: 1%;
1292 height: 1%;
1293 display: block;
1293 display: block;
1294 float: left;
1294 float: left;
1295 color: #515151;
1295 color: #515151;
1296 text-decoration: none;
1296 text-decoration: none;
1297 margin: 0;
1297 margin: 0;
1298 padding: 6px;
1298 padding: 6px;
1299 }
1299 }
1300
1300
1301 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1301 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1302 {
1302 {
1303 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1303 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1304 border-top: 1px solid #ccc;
1304 border-top: 1px solid #ccc;
1305 border-left: 1px solid #bebebe;
1305 border-left: 1px solid #bebebe;
1306 border-right: 1px solid #b1b1b1;
1306 border-right: 1px solid #b1b1b1;
1307 border-bottom: 1px solid #afafaf;
1307 border-bottom: 1px solid #afafaf;
1308 margin: -1px;
1308 margin: -1px;
1309 }
1309 }
1310
1310
1311 #content div.box div.pagination-wh {
1311 #content div.box div.pagination-wh {
1312 height: 1%;
1312 height: 1%;
1313 clear: both;
1313 clear: both;
1314 overflow: hidden;
1314 overflow: hidden;
1315 text-align: right;
1315 text-align: right;
1316 margin: 10px 0 0;
1316 margin: 10px 0 0;
1317 padding: 0;
1317 padding: 0;
1318 }
1318 }
1319
1319
1320 #content div.box div.pagination-right {
1320 #content div.box div.pagination-right {
1321 float: right;
1321 float: right;
1322 }
1322 }
1323
1323
1324 #content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot
1324 #content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot
1325 {
1325 {
1326 height: 1%;
1326 height: 1%;
1327 float: left;
1327 float: left;
1328 background: #ebebeb url("../images/pager.png") repeat-x;
1328 background: #ebebeb url("../images/pager.png") repeat-x;
1329 border-top: 1px solid #dedede;
1329 border-top: 1px solid #dedede;
1330 border-left: 1px solid #cfcfcf;
1330 border-left: 1px solid #cfcfcf;
1331 border-right: 1px solid #c4c4c4;
1331 border-right: 1px solid #c4c4c4;
1332 border-bottom: 1px solid #c4c4c4;
1332 border-bottom: 1px solid #c4c4c4;
1333 color: #4A4A4A;
1333 color: #4A4A4A;
1334 font-weight: 700;
1334 font-weight: 700;
1335 margin: 0 0 0 4px;
1335 margin: 0 0 0 4px;
1336 padding: 6px;
1336 padding: 6px;
1337 }
1337 }
1338
1338
1339 #content div.box div.pagination-wh span.pager_curpage {
1339 #content div.box div.pagination-wh span.pager_curpage {
1340 height: 1%;
1340 height: 1%;
1341 float: left;
1341 float: left;
1342 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1342 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1343 border-top: 1px solid #ccc;
1343 border-top: 1px solid #ccc;
1344 border-left: 1px solid #bebebe;
1344 border-left: 1px solid #bebebe;
1345 border-right: 1px solid #b1b1b1;
1345 border-right: 1px solid #b1b1b1;
1346 border-bottom: 1px solid #afafaf;
1346 border-bottom: 1px solid #afafaf;
1347 color: #515151;
1347 color: #515151;
1348 font-weight: 700;
1348 font-weight: 700;
1349 margin: 0 0 0 4px;
1349 margin: 0 0 0 4px;
1350 padding: 6px;
1350 padding: 6px;
1351 }
1351 }
1352
1352
1353 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1353 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1354 {
1354 {
1355 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1355 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1356 border-top: 1px solid #ccc;
1356 border-top: 1px solid #ccc;
1357 border-left: 1px solid #bebebe;
1357 border-left: 1px solid #bebebe;
1358 border-right: 1px solid #b1b1b1;
1358 border-right: 1px solid #b1b1b1;
1359 border-bottom: 1px solid #afafaf;
1359 border-bottom: 1px solid #afafaf;
1360 text-decoration: none;
1360 text-decoration: none;
1361 }
1361 }
1362
1362
1363 #content div.box div.traffic div.legend {
1363 #content div.box div.traffic div.legend {
1364 clear: both;
1364 clear: both;
1365 overflow: hidden;
1365 overflow: hidden;
1366 border-bottom: 1px solid #ddd;
1366 border-bottom: 1px solid #ddd;
1367 margin: 0 0 10px;
1367 margin: 0 0 10px;
1368 padding: 0 0 10px;
1368 padding: 0 0 10px;
1369 }
1369 }
1370
1370
1371 #content div.box div.traffic div.legend h6 {
1371 #content div.box div.traffic div.legend h6 {
1372 float: left;
1372 float: left;
1373 border: none;
1373 border: none;
1374 margin: 0;
1374 margin: 0;
1375 padding: 0;
1375 padding: 0;
1376 }
1376 }
1377
1377
1378 #content div.box div.traffic div.legend li {
1378 #content div.box div.traffic div.legend li {
1379 list-style: none;
1379 list-style: none;
1380 float: left;
1380 float: left;
1381 font-size: 11px;
1381 font-size: 11px;
1382 margin: 0;
1382 margin: 0;
1383 padding: 0 8px 0 4px;
1383 padding: 0 8px 0 4px;
1384 }
1384 }
1385
1385
1386 #content div.box div.traffic div.legend li.visits {
1386 #content div.box div.traffic div.legend li.visits {
1387 border-left: 12px solid #edc240;
1387 border-left: 12px solid #edc240;
1388 }
1388 }
1389
1389
1390 #content div.box div.traffic div.legend li.pageviews {
1390 #content div.box div.traffic div.legend li.pageviews {
1391 border-left: 12px solid #afd8f8;
1391 border-left: 12px solid #afd8f8;
1392 }
1392 }
1393
1393
1394 #content div.box div.traffic table {
1394 #content div.box div.traffic table {
1395 width: auto;
1395 width: auto;
1396 }
1396 }
1397
1397
1398 #content div.box div.traffic table td {
1398 #content div.box div.traffic table td {
1399 background: transparent;
1399 background: transparent;
1400 border: none;
1400 border: none;
1401 padding: 2px 3px 3px;
1401 padding: 2px 3px 3px;
1402 }
1402 }
1403
1403
1404 #content div.box div.traffic table td.legendLabel {
1404 #content div.box div.traffic table td.legendLabel {
1405 padding: 0 3px 2px;
1405 padding: 0 3px 2px;
1406 }
1406 }
1407
1407
1408 #summary {
1408 #summary {
1409
1409
1410 }
1410 }
1411
1411
1412 #summary .desc {
1412 #summary .desc {
1413 white-space: pre;
1413 white-space: pre;
1414 width: 100%;
1414 width: 100%;
1415 }
1415 }
1416
1416
1417 #summary .repo_name {
1417 #summary .repo_name {
1418 font-size: 1.6em;
1418 font-size: 1.6em;
1419 font-weight: bold;
1419 font-weight: bold;
1420 vertical-align: baseline;
1420 vertical-align: baseline;
1421 clear: right
1421 clear: right
1422 }
1422 }
1423
1423
1424 #footer {
1424 #footer {
1425 clear: both;
1425 clear: both;
1426 overflow: hidden;
1426 overflow: hidden;
1427 text-align: right;
1427 text-align: right;
1428 margin: 0;
1428 margin: 0;
1429 padding: 0 10px 4px;
1429 padding: 0 10px 4px;
1430 margin: -10px 0 0;
1430 margin: -10px 0 0;
1431 }
1431 }
1432
1432
1433 #footer div#footer-inner {
1433 #footer div#footer-inner {
1434 background-color: #eedc94; background-repeat : repeat-x;
1434 background-color: #eedc94; background-repeat : repeat-x;
1435 background-image : -khtml-gradient( linear, left top, left bottom,
1435 background-image : -khtml-gradient( linear, left top, left bottom,
1436 from( #fceec1), to( #eedc94)); background-image : -moz-linear-gradient(
1436 from( #fceec1), to( #eedc94)); background-image : -moz-linear-gradient(
1437 top, #003b76, #00376e); background-image : -ms-linear-gradient( top,
1437 top, #003b76, #00376e); background-image : -ms-linear-gradient( top,
1438 #003b76, #00376e); background-image : -webkit-gradient( linear, left
1438 #003b76, #00376e); background-image : -webkit-gradient( linear, left
1439 top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1439 top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1440 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1440 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1441 background-image : -o-linear-gradient( top, #003b76, #00376e));
1441 background-image : -o-linear-gradient( top, #003b76, #00376e));
1442 background-image : linear-gradient( top, #003b76, #00376e); filter :
1442 background-image : linear-gradient( top, #003b76, #00376e); filter :
1443 progid : DXImageTransform.Microsoft.gradient ( startColorstr =
1443 progid : DXImageTransform.Microsoft.gradient ( startColorstr =
1444 '#003b76', endColorstr = '#00376e', GradientType = 0);
1444 '#003b76', endColorstr = '#00376e', GradientType = 0);
1445 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1445 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1446 -webkit-border-radius: 4px 4px 4px 4px;
1446 -webkit-border-radius: 4px 4px 4px 4px;
1447 -khtml-border-radius: 4px 4px 4px 4px;
1447 -khtml-border-radius: 4px 4px 4px 4px;
1448 -moz-border-radius: 4px 4px 4px 4px;
1448 -moz-border-radius: 4px 4px 4px 4px;
1449 border-radius: 4px 4px 4px 4px;
1449 border-radius: 4px 4px 4px 4px;
1450 background-repeat: repeat-x;
1450 background-repeat: repeat-x;
1451 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
1451 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
1452 to(#eedc94) );
1452 to(#eedc94) );
1453 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1453 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1454 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1454 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1455 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
1455 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
1456 color-stop(100%, #00376e) );
1456 color-stop(100%, #00376e) );
1457 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
1457 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
1458 background-image: -o-linear-gradient(top, #003b76, #00376e) );
1458 background-image: -o-linear-gradient(top, #003b76, #00376e) );
1459 background-image: linear-gradient(top, #003b76, #00376e);
1459 background-image: linear-gradient(top, #003b76, #00376e);
1460 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
1460 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
1461 endColorstr='#00376e', GradientType=0 );
1461 endColorstr='#00376e', GradientType=0 );
1462 }
1462 }
1463
1463
1464 #footer div#footer-inner p {
1464 #footer div#footer-inner p {
1465 padding: 15px 25px 15px 0;
1465 padding: 15px 25px 15px 0;
1466 color: #FFF;
1466 color: #FFF;
1467 font-weight: 700;
1467 font-weight: 700;
1468 }
1468 }
1469
1469
1470 #footer div#footer-inner .footer-link {
1470 #footer div#footer-inner .footer-link {
1471 float: left;
1471 float: left;
1472 padding-left: 10px;
1472 padding-left: 10px;
1473 }
1473 }
1474
1474
1475 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1475 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1476 {
1476 {
1477 color: #FFF;
1477 color: #FFF;
1478 }
1478 }
1479
1479
1480 #login div.title {
1480 #login div.title {
1481 width: 420px;
1481 width: 420px;
1482 clear: both;
1482 clear: both;
1483 overflow: hidden;
1483 overflow: hidden;
1484 position: relative;
1484 position: relative;
1485 background-color: #eedc94; background-repeat : repeat-x;
1485 background-color: #eedc94; background-repeat : repeat-x;
1486 background-image : -khtml-gradient( linear, left top, left bottom,
1486 background-image : -khtml-gradient( linear, left top, left bottom,
1487 from( #fceec1), to( #eedc94)); background-image : -moz-linear-gradient(
1487 from( #fceec1), to( #eedc94)); background-image : -moz-linear-gradient(
1488 top, #003b76, #00376e); background-image : -ms-linear-gradient( top,
1488 top, #003b76, #00376e); background-image : -ms-linear-gradient( top,
1489 #003b76, #00376e); background-image : -webkit-gradient( linear, left
1489 #003b76, #00376e); background-image : -webkit-gradient( linear, left
1490 top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1490 top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1491 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1491 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1492 background-image : -o-linear-gradient( top, #003b76, #00376e));
1492 background-image : -o-linear-gradient( top, #003b76, #00376e));
1493 background-image : linear-gradient( top, #003b76, #00376e); filter :
1493 background-image : linear-gradient( top, #003b76, #00376e); filter :
1494 progid : DXImageTransform.Microsoft.gradient ( startColorstr =
1494 progid : DXImageTransform.Microsoft.gradient ( startColorstr =
1495 '#003b76', endColorstr = '#00376e', GradientType = 0);
1495 '#003b76', endColorstr = '#00376e', GradientType = 0);
1496 margin: 0 auto;
1496 margin: 0 auto;
1497 padding: 0;
1497 padding: 0;
1498 background-repeat: repeat-x;
1498 background-repeat: repeat-x;
1499 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
1499 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
1500 to(#eedc94) );
1500 to(#eedc94) );
1501 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1501 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1502 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1502 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1503 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
1503 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
1504 color-stop(100%, #00376e) );
1504 color-stop(100%, #00376e) );
1505 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
1505 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
1506 background-image: -o-linear-gradient(top, #003b76, #00376e) );
1506 background-image: -o-linear-gradient(top, #003b76, #00376e) );
1507 background-image: linear-gradient(top, #003b76, #00376e);
1507 background-image: linear-gradient(top, #003b76, #00376e);
1508 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
1508 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
1509 endColorstr='#00376e', GradientType=0 );
1509 endColorstr='#00376e', GradientType=0 );
1510 }
1510 }
1511
1511
1512 #login div.inner {
1512 #login div.inner {
1513 width: 380px;
1513 width: 380px;
1514 background: #FFF url("../images/login.png") no-repeat top left;
1514 background: #FFF url("../images/login.png") no-repeat top left;
1515 border-top: none;
1515 border-top: none;
1516 border-bottom: none;
1516 border-bottom: none;
1517 margin: 0 auto;
1517 margin: 0 auto;
1518 padding: 20px;
1518 padding: 20px;
1519 }
1519 }
1520
1520
1521 #login div.form div.fields div.field div.label {
1521 #login div.form div.fields div.field div.label {
1522 width: 173px;
1522 width: 173px;
1523 float: left;
1523 float: left;
1524 text-align: right;
1524 text-align: right;
1525 margin: 2px 10px 0 0;
1525 margin: 2px 10px 0 0;
1526 padding: 5px 0 0 5px;
1526 padding: 5px 0 0 5px;
1527 }
1527 }
1528
1528
1529 #login div.form div.fields div.field div.input input {
1529 #login div.form div.fields div.field div.input input {
1530 width: 176px;
1530 width: 176px;
1531 background: #FFF;
1531 background: #FFF;
1532 border-top: 1px solid #b3b3b3;
1532 border-top: 1px solid #b3b3b3;
1533 border-left: 1px solid #b3b3b3;
1533 border-left: 1px solid #b3b3b3;
1534 border-right: 1px solid #eaeaea;
1534 border-right: 1px solid #eaeaea;
1535 border-bottom: 1px solid #eaeaea;
1535 border-bottom: 1px solid #eaeaea;
1536 color: #000;
1536 color: #000;
1537 font-size: 11px;
1537 font-size: 11px;
1538 margin: 0;
1538 margin: 0;
1539 padding: 7px 7px 6px;
1539 padding: 7px 7px 6px;
1540 }
1540 }
1541
1541
1542 #login div.form div.fields div.buttons {
1542 #login div.form div.fields div.buttons {
1543 clear: both;
1543 clear: both;
1544 overflow: hidden;
1544 overflow: hidden;
1545 border-top: 1px solid #DDD;
1545 border-top: 1px solid #DDD;
1546 text-align: right;
1546 text-align: right;
1547 margin: 0;
1547 margin: 0;
1548 padding: 10px 0 0;
1548 padding: 10px 0 0;
1549 }
1549 }
1550
1550
1551 #login div.form div.links {
1551 #login div.form div.links {
1552 clear: both;
1552 clear: both;
1553 overflow: hidden;
1553 overflow: hidden;
1554 margin: 10px 0 0;
1554 margin: 10px 0 0;
1555 padding: 0 0 2px;
1555 padding: 0 0 2px;
1556 }
1556 }
1557
1557
1558 #quick_login {
1558 #quick_login {
1559 top: 31px;
1559 top: 31px;
1560 background-color: rgb(0, 51, 103);
1560 background-color: rgb(0, 51, 103);
1561 z-index: 999;
1561 z-index: 999;
1562 height: 150px;
1562 height: 150px;
1563 position: absolute;
1563 position: absolute;
1564 margin-left: -16px;
1564 margin-left: -16px;
1565 width: 281px;
1565 width: 281px;
1566 -webkit-border-radius: 0px 0px 4px 4px;
1566 -webkit-border-radius: 0px 0px 4px 4px;
1567 -khtml-border-radius: 0px 0px 4px 4px;
1567 -khtml-border-radius: 0px 0px 4px 4px;
1568 -moz-border-radius: 0px 0px 4px 4px;
1568 -moz-border-radius: 0px 0px 4px 4px;
1569 border-radius: 0px 0px 4px 4px;
1569 border-radius: 0px 0px 4px 4px;
1570 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1570 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1571 }
1571 }
1572
1572
1573 #quick_login .password_forgoten {
1573 #quick_login .password_forgoten {
1574 padding-right: 10px;
1574 padding-right: 10px;
1575 padding-top: 0px;
1575 padding-top: 0px;
1576 float: left;
1576 float: left;
1577 }
1577 }
1578
1578
1579 #quick_login .password_forgoten a {
1579 #quick_login .password_forgoten a {
1580 font-size: 10px
1580 font-size: 10px
1581 }
1581 }
1582
1582
1583 #quick_login .register {
1583 #quick_login .register {
1584 padding-right: 10px;
1584 padding-right: 10px;
1585 padding-top: 5px;
1585 padding-top: 5px;
1586 float: left;
1586 float: left;
1587 }
1587 }
1588
1588
1589 #quick_login .register a {
1589 #quick_login .register a {
1590 font-size: 10px
1590 font-size: 10px
1591 }
1591 }
1592
1592
1593 #quick_login div.form div.fields {
1593 #quick_login div.form div.fields {
1594 padding-top: 2px;
1594 padding-top: 2px;
1595 padding-left: 10px;
1595 padding-left: 10px;
1596 }
1596 }
1597
1597
1598 #quick_login div.form div.fields div.field {
1598 #quick_login div.form div.fields div.field {
1599 padding: 5px;
1599 padding: 5px;
1600 }
1600 }
1601
1601
1602 #quick_login div.form div.fields div.field div.label label {
1602 #quick_login div.form div.fields div.field div.label label {
1603 color: #fff;
1603 color: #fff;
1604 padding-bottom: 3px;
1604 padding-bottom: 3px;
1605 }
1605 }
1606
1606
1607 #quick_login div.form div.fields div.field div.input input {
1607 #quick_login div.form div.fields div.field div.input input {
1608 width: 236px;
1608 width: 236px;
1609 background: #FFF;
1609 background: #FFF;
1610 border-top: 1px solid #b3b3b3;
1610 border-top: 1px solid #b3b3b3;
1611 border-left: 1px solid #b3b3b3;
1611 border-left: 1px solid #b3b3b3;
1612 border-right: 1px solid #eaeaea;
1612 border-right: 1px solid #eaeaea;
1613 border-bottom: 1px solid #eaeaea;
1613 border-bottom: 1px solid #eaeaea;
1614 color: #000;
1614 color: #000;
1615 font-size: 11px;
1615 font-size: 11px;
1616 margin: 0;
1616 margin: 0;
1617 padding: 5px 7px 4px;
1617 padding: 5px 7px 4px;
1618 }
1618 }
1619
1619
1620 #quick_login div.form div.fields div.buttons {
1620 #quick_login div.form div.fields div.buttons {
1621 clear: both;
1621 clear: both;
1622 overflow: hidden;
1622 overflow: hidden;
1623 text-align: right;
1623 text-align: right;
1624 margin: 0;
1624 margin: 0;
1625 padding: 10px 14px 0px 5px;
1625 padding: 10px 14px 0px 5px;
1626 }
1626 }
1627
1627
1628 #quick_login div.form div.links {
1628 #quick_login div.form div.links {
1629 clear: both;
1629 clear: both;
1630 overflow: hidden;
1630 overflow: hidden;
1631 margin: 10px 0 0;
1631 margin: 10px 0 0;
1632 padding: 0 0 2px;
1632 padding: 0 0 2px;
1633 }
1633 }
1634
1634
1635 #register div.title {
1635 #register div.title {
1636 clear: both;
1636 clear: both;
1637 overflow: hidden;
1637 overflow: hidden;
1638 position: relative;
1638 position: relative;
1639 background-color: #eedc94;
1639 background-color: #eedc94;
1640 background-repeat: repeat-x;
1640 background-repeat: repeat-x;
1641 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
1641 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
1642 to(#eedc94) );
1642 to(#eedc94) );
1643 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1643 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1644 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1644 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1645 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
1645 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),
1646 color-stop(100%, #00376e) );
1646 color-stop(100%, #00376e) );
1647 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
1647 background-image: -webkit-linear-gradient(top, #003b76, #00376e) );
1648 background-image: -o-linear-gradient(top, #003b76, #00376e) );
1648 background-image: -o-linear-gradient(top, #003b76, #00376e) );
1649 background-image: linear-gradient(top, #003b76, #00376e);
1649 background-image: linear-gradient(top, #003b76, #00376e);
1650 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
1650 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
1651 endColorstr='#00376e', GradientType=0 );
1651 endColorstr='#00376e', GradientType=0 );
1652 margin: 0 auto;
1652 margin: 0 auto;
1653 padding: 0;
1653 padding: 0;
1654 }
1654 }
1655
1655
1656 #register div.inner {
1656 #register div.inner {
1657 background: #FFF;
1657 background: #FFF;
1658 border-top: none;
1658 border-top: none;
1659 border-bottom: none;
1659 border-bottom: none;
1660 margin: 0 auto;
1660 margin: 0 auto;
1661 padding: 20px;
1661 padding: 20px;
1662 }
1662 }
1663
1663
1664 #register div.form div.fields div.field div.label {
1664 #register div.form div.fields div.field div.label {
1665 width: 135px;
1665 width: 135px;
1666 float: left;
1666 float: left;
1667 text-align: right;
1667 text-align: right;
1668 margin: 2px 10px 0 0;
1668 margin: 2px 10px 0 0;
1669 padding: 5px 0 0 5px;
1669 padding: 5px 0 0 5px;
1670 }
1670 }
1671
1671
1672 #register div.form div.fields div.field div.input input {
1672 #register div.form div.fields div.field div.input input {
1673 width: 300px;
1673 width: 300px;
1674 background: #FFF;
1674 background: #FFF;
1675 border-top: 1px solid #b3b3b3;
1675 border-top: 1px solid #b3b3b3;
1676 border-left: 1px solid #b3b3b3;
1676 border-left: 1px solid #b3b3b3;
1677 border-right: 1px solid #eaeaea;
1677 border-right: 1px solid #eaeaea;
1678 border-bottom: 1px solid #eaeaea;
1678 border-bottom: 1px solid #eaeaea;
1679 color: #000;
1679 color: #000;
1680 font-size: 11px;
1680 font-size: 11px;
1681 margin: 0;
1681 margin: 0;
1682 padding: 7px 7px 6px;
1682 padding: 7px 7px 6px;
1683 }
1683 }
1684
1684
1685 #register div.form div.fields div.buttons {
1685 #register div.form div.fields div.buttons {
1686 clear: both;
1686 clear: both;
1687 overflow: hidden;
1687 overflow: hidden;
1688 border-top: 1px solid #DDD;
1688 border-top: 1px solid #DDD;
1689 text-align: left;
1689 text-align: left;
1690 margin: 0;
1690 margin: 0;
1691 padding: 10px 0 0 150px;
1691 padding: 10px 0 0 150px;
1692 }
1692 }
1693
1693
1694 #register div.form div.activation_msg {
1694 #register div.form div.activation_msg {
1695 padding-top: 4px;
1695 padding-top: 4px;
1696 padding-bottom: 4px;
1696 padding-bottom: 4px;
1697 }
1697 }
1698
1698
1699 #journal .journal_day {
1699 #journal .journal_day {
1700 font-size: 20px;
1700 font-size: 20px;
1701 padding: 10px 0px;
1701 padding: 10px 0px;
1702 border-bottom: 2px solid #DDD;
1702 border-bottom: 2px solid #DDD;
1703 margin-left: 10px;
1703 margin-left: 10px;
1704 margin-right: 10px;
1704 margin-right: 10px;
1705 }
1705 }
1706
1706
1707 #journal .journal_container {
1707 #journal .journal_container {
1708 padding: 5px;
1708 padding: 5px;
1709 clear: both;
1709 clear: both;
1710 margin: 0px 5px 0px 10px;
1710 margin: 0px 5px 0px 10px;
1711 }
1711 }
1712
1712
1713 #journal .journal_action_container {
1713 #journal .journal_action_container {
1714 padding-left: 38px;
1714 padding-left: 38px;
1715 }
1715 }
1716
1716
1717 #journal .journal_user {
1717 #journal .journal_user {
1718 color: #747474;
1718 color: #747474;
1719 font-size: 14px;
1719 font-size: 14px;
1720 font-weight: bold;
1720 font-weight: bold;
1721 height: 30px;
1721 height: 30px;
1722 }
1722 }
1723
1723
1724 #journal .journal_icon {
1724 #journal .journal_icon {
1725 clear: both;
1725 clear: both;
1726 float: left;
1726 float: left;
1727 padding-right: 4px;
1727 padding-right: 4px;
1728 padding-top: 3px;
1728 padding-top: 3px;
1729 }
1729 }
1730
1730
1731 #journal .journal_action {
1731 #journal .journal_action {
1732 padding-top: 4px;
1732 padding-top: 4px;
1733 min-height: 2px;
1733 min-height: 2px;
1734 float: left
1734 float: left
1735 }
1735 }
1736
1736
1737 #journal .journal_action_params {
1737 #journal .journal_action_params {
1738 clear: left;
1738 clear: left;
1739 padding-left: 22px;
1739 padding-left: 22px;
1740 }
1740 }
1741
1741
1742 #journal .journal_repo {
1742 #journal .journal_repo {
1743 float: left;
1743 float: left;
1744 margin-left: 6px;
1744 margin-left: 6px;
1745 padding-top: 3px;
1745 padding-top: 3px;
1746 }
1746 }
1747
1747
1748 #journal .date {
1748 #journal .date {
1749 clear: both;
1749 clear: both;
1750 color: #777777;
1750 color: #777777;
1751 font-size: 11px;
1751 font-size: 11px;
1752 padding-left: 22px;
1752 padding-left: 22px;
1753 }
1753 }
1754
1754
1755 #journal .journal_repo .journal_repo_name {
1755 #journal .journal_repo .journal_repo_name {
1756 font-weight: bold;
1756 font-weight: bold;
1757 font-size: 1.1em;
1757 font-size: 1.1em;
1758 }
1758 }
1759
1759
1760 #journal .compare_view {
1760 #journal .compare_view {
1761 padding: 5px 0px 5px 0px;
1761 padding: 5px 0px 5px 0px;
1762 width: 95px;
1762 width: 95px;
1763 }
1763 }
1764
1764
1765 .journal_highlight {
1765 .journal_highlight {
1766 font-weight: bold;
1766 font-weight: bold;
1767 padding: 0 2px;
1767 padding: 0 2px;
1768 vertical-align: bottom;
1768 vertical-align: bottom;
1769 }
1769 }
1770
1770
1771 .trending_language_tbl,.trending_language_tbl td {
1771 .trending_language_tbl,.trending_language_tbl td {
1772 border: 0 !important;
1772 border: 0 !important;
1773 margin: 0 !important;
1773 margin: 0 !important;
1774 padding: 0 !important;
1774 padding: 0 !important;
1775 }
1775 }
1776
1776
1777 .trending_language {
1777 .trending_language {
1778 background-color: #003367;
1778 background-color: #003367;
1779 color: #FFF;
1779 color: #FFF;
1780 display: block;
1780 display: block;
1781 min-width: 20px;
1781 min-width: 20px;
1782 text-decoration: none;
1782 text-decoration: none;
1783 height: 12px;
1783 height: 12px;
1784 margin-bottom: 4px;
1784 margin-bottom: 4px;
1785 margin-left: 5px;
1785 margin-left: 5px;
1786 white-space: pre;
1786 white-space: pre;
1787 padding: 3px;
1787 padding: 3px;
1788 }
1788 }
1789
1789
1790 h3.files_location {
1790 h3.files_location {
1791 font-size: 1.8em;
1791 font-size: 1.8em;
1792 font-weight: 700;
1792 font-weight: 700;
1793 border-bottom: none !important;
1793 border-bottom: none !important;
1794 margin: 10px 0 !important;
1794 margin: 10px 0 !important;
1795 }
1795 }
1796
1796
1797 #files_data dl dt {
1797 #files_data dl dt {
1798 float: left;
1798 float: left;
1799 width: 115px;
1799 width: 115px;
1800 margin: 0 !important;
1800 margin: 0 !important;
1801 padding: 5px;
1801 padding: 5px;
1802 }
1802 }
1803
1803
1804 #files_data dl dd {
1804 #files_data dl dd {
1805 margin: 0 !important;
1805 margin: 0 !important;
1806 padding: 5px !important;
1806 padding: 5px !important;
1807 }
1807 }
1808
1808
1809 #changeset_content {
1809 #changeset_content {
1810 border: 1px solid #CCC;
1810 border: 1px solid #CCC;
1811 padding: 5px;
1811 padding: 5px;
1812 }
1812 }
1813
1813
1814 #changeset_compare_view_content {
1814 #changeset_compare_view_content {
1815 border: 1px solid #CCC;
1815 border: 1px solid #CCC;
1816 padding: 5px;
1816 padding: 5px;
1817 }
1817 }
1818
1818
1819 #changeset_content .container {
1819 #changeset_content .container {
1820 min-height: 120px;
1820 min-height: 120px;
1821 font-size: 1.2em;
1821 font-size: 1.2em;
1822 overflow: hidden;
1822 overflow: hidden;
1823 }
1823 }
1824
1824
1825 #changeset_compare_view_content .compare_view_commits {
1825 #changeset_compare_view_content .compare_view_commits {
1826 width: auto !important;
1826 width: auto !important;
1827 }
1827 }
1828
1828
1829 #changeset_compare_view_content .compare_view_commits td {
1829 #changeset_compare_view_content .compare_view_commits td {
1830 padding: 0px 0px 0px 12px !important;
1830 padding: 0px 0px 0px 12px !important;
1831 }
1831 }
1832
1832
1833 #changeset_content .container .right {
1833 #changeset_content .container .right {
1834 float: right;
1834 float: right;
1835 width: 25%;
1835 width: 25%;
1836 text-align: right;
1836 text-align: right;
1837 }
1837 }
1838
1838
1839 #changeset_content .container .left .message {
1839 #changeset_content .container .left .message {
1840 font-style: italic;
1840 font-style: italic;
1841 color: #556CB5;
1841 color: #556CB5;
1842 white-space: pre-wrap;
1842 white-space: pre-wrap;
1843 }
1843 }
1844
1844
1845 .cs_files .cur_cs {
1845 .cs_files .cur_cs {
1846 margin: 10px 2px;
1846 margin: 10px 2px;
1847 font-weight: bold;
1847 font-weight: bold;
1848 }
1848 }
1849
1849
1850 .cs_files .node {
1850 .cs_files .node {
1851 float: left;
1851 float: left;
1852 }
1852 }
1853
1853
1854 .cs_files .changes {
1854 .cs_files .changes {
1855 float: right;
1855 float: right;
1856 color:#003367;
1856 color:#003367;
1857
1857
1858 }
1858 }
1859
1859
1860 .cs_files .changes .added {
1860 .cs_files .changes .added {
1861 background-color: #BBFFBB;
1861 background-color: #BBFFBB;
1862 float: left;
1862 float: left;
1863 text-align: center;
1863 text-align: center;
1864 font-size: 9px;
1864 font-size: 9px;
1865 padding: 2px 0px 2px 0px;
1865 padding: 2px 0px 2px 0px;
1866 }
1866 }
1867
1867
1868 .cs_files .changes .deleted {
1868 .cs_files .changes .deleted {
1869 background-color: #FF8888;
1869 background-color: #FF8888;
1870 float: left;
1870 float: left;
1871 text-align: center;
1871 text-align: center;
1872 font-size: 9px;
1872 font-size: 9px;
1873 padding: 2px 0px 2px 0px;
1873 padding: 2px 0px 2px 0px;
1874 }
1874 }
1875
1875
1876 .cs_files .cs_added {
1876 .cs_files .cs_added {
1877 background: url("../images/icons/page_white_add.png") no-repeat scroll
1877 background: url("../images/icons/page_white_add.png") no-repeat scroll
1878 3px;
1878 3px;
1879 height: 16px;
1879 height: 16px;
1880 padding-left: 20px;
1880 padding-left: 20px;
1881 margin-top: 7px;
1881 margin-top: 7px;
1882 text-align: left;
1882 text-align: left;
1883 }
1883 }
1884
1884
1885 .cs_files .cs_changed {
1885 .cs_files .cs_changed {
1886 background: url("../images/icons/page_white_edit.png") no-repeat scroll
1886 background: url("../images/icons/page_white_edit.png") no-repeat scroll
1887 3px;
1887 3px;
1888 height: 16px;
1888 height: 16px;
1889 padding-left: 20px;
1889 padding-left: 20px;
1890 margin-top: 7px;
1890 margin-top: 7px;
1891 text-align: left;
1891 text-align: left;
1892 }
1892 }
1893
1893
1894 .cs_files .cs_removed {
1894 .cs_files .cs_removed {
1895 background: url("../images/icons/page_white_delete.png") no-repeat
1895 background: url("../images/icons/page_white_delete.png") no-repeat
1896 scroll 3px;
1896 scroll 3px;
1897 height: 16px;
1897 height: 16px;
1898 padding-left: 20px;
1898 padding-left: 20px;
1899 margin-top: 7px;
1899 margin-top: 7px;
1900 text-align: left;
1900 text-align: left;
1901 }
1901 }
1902
1902
1903 #graph {
1903 #graph {
1904 overflow: hidden;
1904 overflow: hidden;
1905 }
1905 }
1906
1906
1907 #graph_nodes {
1907 #graph_nodes {
1908 float: left;
1908 float: left;
1909 margin-right: -6px;
1909 margin-right: -6px;
1910 margin-top: 0px;
1910 margin-top: 0px;
1911 }
1911 }
1912
1912
1913 #graph_content {
1913 #graph_content {
1914 width: 800px;
1914 width: 800px;
1915 float: left;
1915 float: left;
1916 }
1916 }
1917
1917
1918 #graph_content .container_header {
1918 #graph_content .container_header {
1919 border: 1px solid #CCC;
1919 border: 1px solid #CCC;
1920 padding: 10px;
1920 padding: 10px;
1921 height: 45px;
1921 height: 45px;
1922 -webkit-border-radius: 6px 6px 0px 0px;
1922 -webkit-border-radius: 6px 6px 0px 0px;
1923 -moz-border-radius: 6px 6px 0px 0px;
1923 -moz-border-radius: 6px 6px 0px 0px;
1924 border-radius: 6px 6px 0px 0px;
1924 border-radius: 6px 6px 0px 0px;
1925 }
1925 }
1926
1926
1927 #graph_content #rev_range_container {
1927 #graph_content #rev_range_container {
1928 padding: 10px 0px;
1928 padding: 10px 0px;
1929 clear: both;
1929 clear: both;
1930 }
1930 }
1931
1931
1932 #graph_content .container {
1932 #graph_content .container {
1933 border-bottom: 1px solid #CCC;
1933 border-bottom: 1px solid #CCC;
1934 border-left: 1px solid #CCC;
1934 border-left: 1px solid #CCC;
1935 border-right: 1px solid #CCC;
1935 border-right: 1px solid #CCC;
1936 min-height: 70px;
1936 min-height: 70px;
1937 overflow: hidden;
1937 overflow: hidden;
1938 font-size: 1.2em;
1938 font-size: 1.2em;
1939 }
1939 }
1940
1940
1941 #graph_content .container .right {
1941 #graph_content .container .right {
1942 float: right;
1942 float: right;
1943 width: 28%;
1943 width: 28%;
1944 text-align: right;
1944 text-align: right;
1945 padding-bottom: 5px;
1945 padding-bottom: 5px;
1946 }
1946 }
1947
1947
1948 #graph_content .container .left .date {
1948 #graph_content .container .left .date {
1949 font-weight: 700;
1949 font-weight: 700;
1950 padding-bottom: 5px;
1950 padding-bottom: 5px;
1951 }
1951 }
1952
1952
1953 #graph_content .container .left .date span {
1953 #graph_content .container .left .date span {
1954 vertical-align: text-top;
1954 vertical-align: text-top;
1955 }
1955 }
1956
1956
1957 #graph_content .container .left .author {
1957 #graph_content .container .left .author {
1958 height: 22px;
1958 height: 22px;
1959 }
1959 }
1960
1960
1961 #graph_content .container .left .author .user {
1961 #graph_content .container .left .author .user {
1962 color: #444444;
1962 color: #444444;
1963 float: left;
1963 float: left;
1964 font-size: 12px;
1964 font-size: 12px;
1965 margin-left: -4px;
1965 margin-left: -4px;
1966 margin-top: 4px;
1966 margin-top: 4px;
1967 }
1967 }
1968
1968
1969 #graph_content .container .left .message {
1969 #graph_content .container .left .message {
1970 font-size: 100%;
1970 font-size: 100%;
1971 padding-top: 3px;
1971 padding-top: 3px;
1972 white-space: pre-wrap;
1972 white-space: pre-wrap;
1973 }
1973 }
1974
1974
1975 #graph_content .container .left .message a:hover{
1975 #graph_content .container .left .message a:hover{
1976 text-decoration: none;
1976 text-decoration: none;
1977 }
1977 }
1978
1978
1979 .right div {
1979 .right div {
1980 clear: both;
1980 clear: both;
1981 }
1981 }
1982
1982
1983 .right .changes .changed_total {
1983 .right .changes .changed_total {
1984 border: 0px solid #DDD;
1984 border: 0px solid #DDD;
1985 display: block;
1985 display: block;
1986 float: right;
1986 float: right;
1987 text-align: center;
1987 text-align: center;
1988 min-width: 45px;
1988 min-width: 45px;
1989 cursor: pointer;
1989 cursor: pointer;
1990 background: #FD8;
1990 background: #FD8;
1991 font-weight: bold;
1991 font-weight: bold;
1992 -webkit-border-radius: 0px 0px 0px 6px;
1992 -webkit-border-radius: 0px 0px 0px 6px;
1993 -moz-border-radius: 0px 0px 0px 6px;
1993 -moz-border-radius: 0px 0px 0px 6px;
1994 border-radius: 0px 0px 0px 6px;
1994 border-radius: 0px 0px 0px 6px;
1995 padding: 2px;
1995 padding: 2px;
1996 }
1996 }
1997
1997
1998 .right .changes .added,.changed,.removed {
1998 .right .changes .added,.changed,.removed {
1999 border: 1px solid #DDD;
1999 border: 1px solid #DDD;
2000 display: block;
2000 display: block;
2001 float: right;
2001 float: right;
2002 text-align: center;
2002 text-align: center;
2003 min-width: 15px;
2003 min-width: 15px;
2004 cursor: help;
2004 cursor: help;
2005 }
2005 }
2006
2006
2007 .right .changes .large {
2007 .right .changes .large {
2008 border: 1px solid #DDD;
2008 border: 1px solid #DDD;
2009 display: block;
2009 display: block;
2010 float: right;
2010 float: right;
2011 text-align: center;
2011 text-align: center;
2012 min-width: 45px;
2012 min-width: 45px;
2013 cursor: help;
2013 cursor: help;
2014 background: #54A9F7;
2014 background: #54A9F7;
2015 }
2015 }
2016
2016
2017 .right .changes .added {
2017 .right .changes .added {
2018 background: #BFB;
2018 background: #BFB;
2019 }
2019 }
2020
2020
2021 .right .changes .changed {
2021 .right .changes .changed {
2022 background: #FD8;
2022 background: #FD8;
2023 }
2023 }
2024
2024
2025 .right .changes .removed {
2025 .right .changes .removed {
2026 background: #F88;
2026 background: #F88;
2027 }
2027 }
2028
2028
2029 .right .merge {
2029 .right .merge {
2030 vertical-align: top;
2030 vertical-align: top;
2031 font-size: 0.75em;
2031 font-size: 0.75em;
2032 font-weight: 700;
2032 font-weight: 700;
2033 }
2033 }
2034
2034
2035 .right .parent {
2035 .right .parent {
2036 font-size: 90%;
2036 font-size: 90%;
2037 font-family: monospace;
2037 font-family: monospace;
2038 padding: 2px 2px 2px 2px;
2038 padding: 2px 2px 2px 2px;
2039 }
2039 }
2040 .right .logtags{
2040 .right .logtags{
2041 padding: 2px 2px 2px 2px;
2041 padding: 2px 2px 2px 2px;
2042 }
2042 }
2043 .right .logtags .branchtag,.logtags .branchtag {
2043 .right .logtags .branchtag,.logtags .branchtag {
2044 padding: 1px 3px 2px;
2044 padding: 1px 3px 2px;
2045 background-color: #bfbfbf;
2045 background-color: #bfbfbf;
2046 font-size: 9.75px;
2046 font-size: 9.75px;
2047 font-weight: bold;
2047 font-weight: bold;
2048 color: #ffffff;
2048 color: #ffffff;
2049 text-transform: uppercase;
2049 text-transform: uppercase;
2050 white-space: nowrap;
2050 white-space: nowrap;
2051 -webkit-border-radius: 3px;
2051 -webkit-border-radius: 3px;
2052 -moz-border-radius: 3px;
2052 -moz-border-radius: 3px;
2053 border-radius: 3px;
2053 border-radius: 3px;
2054 padding-left:4px;
2054 padding-left:4px;
2055 }
2055 }
2056 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2056 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2057 text-decoration: none;
2057 text-decoration: none;
2058 }
2058 }
2059 .right .logtags .tagtag,.logtags .tagtag {
2059 .right .logtags .tagtag,.logtags .tagtag {
2060 padding: 1px 3px 2px;
2060 padding: 1px 3px 2px;
2061 background-color: #62cffc;
2061 background-color: #62cffc;
2062 font-size: 9.75px;
2062 font-size: 9.75px;
2063 font-weight: bold;
2063 font-weight: bold;
2064 color: #ffffff;
2064 color: #ffffff;
2065 text-transform: uppercase;
2065 text-transform: uppercase;
2066 white-space: nowrap;
2066 white-space: nowrap;
2067 -webkit-border-radius: 3px;
2067 -webkit-border-radius: 3px;
2068 -moz-border-radius: 3px;
2068 -moz-border-radius: 3px;
2069 border-radius: 3px;
2069 border-radius: 3px;
2070 }
2070 }
2071 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2071 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2072 text-decoration: none;
2072 text-decoration: none;
2073 }
2073 }
2074 div.browserblock {
2074 div.browserblock {
2075 overflow: hidden;
2075 overflow: hidden;
2076 border: 1px solid #ccc;
2076 border: 1px solid #ccc;
2077 background: #f8f8f8;
2077 background: #f8f8f8;
2078 font-size: 100%;
2078 font-size: 100%;
2079 line-height: 125%;
2079 line-height: 125%;
2080 padding: 0;
2080 padding: 0;
2081 }
2081 }
2082
2082
2083 div.browserblock .browser-header {
2083 div.browserblock .browser-header {
2084 background: #FFF;
2084 background: #FFF;
2085 padding: 10px 0px 15px 0px;
2085 padding: 10px 0px 15px 0px;
2086 width: 100%;
2086 width: 100%;
2087 }
2087 }
2088
2088
2089 div.browserblock .browser-nav {
2089 div.browserblock .browser-nav {
2090 float: left
2090 float: left
2091 }
2091 }
2092
2092
2093 div.browserblock .browser-branch {
2093 div.browserblock .browser-branch {
2094 float: left;
2094 float: left;
2095 }
2095 }
2096
2096
2097 div.browserblock .browser-branch label {
2097 div.browserblock .browser-branch label {
2098 color: #4A4A4A;
2098 color: #4A4A4A;
2099 vertical-align: text-top;
2099 vertical-align: text-top;
2100 }
2100 }
2101
2101
2102 div.browserblock .browser-header span {
2102 div.browserblock .browser-header span {
2103 margin-left: 5px;
2103 margin-left: 5px;
2104 font-weight: 700;
2104 font-weight: 700;
2105 }
2105 }
2106
2106
2107 div.browserblock .browser-search {
2107 div.browserblock .browser-search {
2108 clear: both;
2108 clear: both;
2109 padding: 8px 8px 0px 5px;
2109 padding: 8px 8px 0px 5px;
2110 height: 20px;
2110 height: 20px;
2111 }
2111 }
2112
2112
2113 div.browserblock #node_filter_box {
2113 div.browserblock #node_filter_box {
2114
2114
2115 }
2115 }
2116
2116
2117 div.browserblock .search_activate {
2117 div.browserblock .search_activate {
2118 float: left
2118 float: left
2119 }
2119 }
2120
2120
2121 div.browserblock .add_node {
2121 div.browserblock .add_node {
2122 float: left;
2122 float: left;
2123 padding-left: 5px;
2123 padding-left: 5px;
2124 }
2124 }
2125
2125
2126 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2126 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2127 {
2127 {
2128 text-decoration: none !important;
2128 text-decoration: none !important;
2129 }
2129 }
2130
2130
2131 div.browserblock .browser-body {
2131 div.browserblock .browser-body {
2132 background: #EEE;
2132 background: #EEE;
2133 border-top: 1px solid #CCC;
2133 border-top: 1px solid #CCC;
2134 }
2134 }
2135
2135
2136 table.code-browser {
2136 table.code-browser {
2137 border-collapse: collapse;
2137 border-collapse: collapse;
2138 width: 100%;
2138 width: 100%;
2139 }
2139 }
2140
2140
2141 table.code-browser tr {
2141 table.code-browser tr {
2142 margin: 3px;
2142 margin: 3px;
2143 }
2143 }
2144
2144
2145 table.code-browser thead th {
2145 table.code-browser thead th {
2146 background-color: #EEE;
2146 background-color: #EEE;
2147 height: 20px;
2147 height: 20px;
2148 font-size: 1.1em;
2148 font-size: 1.1em;
2149 font-weight: 700;
2149 font-weight: 700;
2150 text-align: left;
2150 text-align: left;
2151 padding-left: 10px;
2151 padding-left: 10px;
2152 }
2152 }
2153
2153
2154 table.code-browser tbody td {
2154 table.code-browser tbody td {
2155 padding-left: 10px;
2155 padding-left: 10px;
2156 height: 20px;
2156 height: 20px;
2157 }
2157 }
2158
2158
2159 table.code-browser .browser-file {
2159 table.code-browser .browser-file {
2160 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2160 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2161 height: 16px;
2161 height: 16px;
2162 padding-left: 20px;
2162 padding-left: 20px;
2163 text-align: left;
2163 text-align: left;
2164 }
2164 }
2165
2165
2166 .diffblock .changeset_file {
2166 .diffblock .changeset_file {
2167 background: url("../images/icons/file.png") no-repeat scroll 3px;
2167 background: url("../images/icons/file.png") no-repeat scroll 3px;
2168 height: 16px;
2168 height: 16px;
2169 padding-left: 22px;
2169 padding-left: 22px;
2170 text-align: left;
2170 text-align: left;
2171 font-size: 14px;
2171 font-size: 14px;
2172 }
2172 }
2173
2173
2174 .diffblock .changeset_header {
2174 .diffblock .changeset_header {
2175 margin-left: 6px !important;
2175 margin-left: 6px !important;
2176 }
2176 }
2177
2177
2178 table.code-browser .browser-dir {
2178 table.code-browser .browser-dir {
2179 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2179 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2180 height: 16px;
2180 height: 16px;
2181 padding-left: 20px;
2181 padding-left: 20px;
2182 text-align: left;
2182 text-align: left;
2183 }
2183 }
2184
2184
2185 .box .search {
2185 .box .search {
2186 clear: both;
2186 clear: both;
2187 overflow: hidden;
2187 overflow: hidden;
2188 margin: 0;
2188 margin: 0;
2189 padding: 0 20px 10px;
2189 padding: 0 20px 10px;
2190 }
2190 }
2191
2191
2192 .box .search div.search_path {
2192 .box .search div.search_path {
2193 background: none repeat scroll 0 0 #EEE;
2193 background: none repeat scroll 0 0 #EEE;
2194 border: 1px solid #CCC;
2194 border: 1px solid #CCC;
2195 color: blue;
2195 color: blue;
2196 margin-bottom: 10px;
2196 margin-bottom: 10px;
2197 padding: 10px 0;
2197 padding: 10px 0;
2198 }
2198 }
2199
2199
2200 .box .search div.search_path div.link {
2200 .box .search div.search_path div.link {
2201 font-weight: 700;
2201 font-weight: 700;
2202 margin-left: 25px;
2202 margin-left: 25px;
2203 }
2203 }
2204
2204
2205 .box .search div.search_path div.link a {
2205 .box .search div.search_path div.link a {
2206 color: #003367;
2206 color: #003367;
2207 cursor: pointer;
2207 cursor: pointer;
2208 text-decoration: none;
2208 text-decoration: none;
2209 }
2209 }
2210
2210
2211 #path_unlock {
2211 #path_unlock {
2212 color: red;
2212 color: red;
2213 font-size: 1.2em;
2213 font-size: 1.2em;
2214 padding-left: 4px;
2214 padding-left: 4px;
2215 }
2215 }
2216
2216
2217 .info_box span {
2217 .info_box span {
2218 margin-left: 3px;
2218 margin-left: 3px;
2219 margin-right: 3px;
2219 margin-right: 3px;
2220 }
2220 }
2221
2221
2222 .info_box .rev {
2222 .info_box .rev {
2223 color: #003367;
2223 color: #003367;
2224 font-size: 1.6em;
2224 font-size: 1.6em;
2225 font-weight: bold;
2225 font-weight: bold;
2226 vertical-align: sub;
2226 vertical-align: sub;
2227 }
2227 }
2228
2228
2229 .info_box input#at_rev,.info_box input#size {
2229 .info_box input#at_rev,.info_box input#size {
2230 background: #FFF;
2230 background: #FFF;
2231 border-top: 1px solid #b3b3b3;
2231 border-top: 1px solid #b3b3b3;
2232 border-left: 1px solid #b3b3b3;
2232 border-left: 1px solid #b3b3b3;
2233 border-right: 1px solid #eaeaea;
2233 border-right: 1px solid #eaeaea;
2234 border-bottom: 1px solid #eaeaea;
2234 border-bottom: 1px solid #eaeaea;
2235 color: #000;
2235 color: #000;
2236 font-size: 12px;
2236 font-size: 12px;
2237 margin: 0;
2237 margin: 0;
2238 padding: 1px 5px 1px;
2238 padding: 1px 5px 1px;
2239 }
2239 }
2240
2240
2241 .info_box input#view {
2241 .info_box input#view {
2242 text-align: center;
2242 text-align: center;
2243 padding: 4px 3px 2px 2px;
2243 padding: 4px 3px 2px 2px;
2244 }
2244 }
2245
2245
2246 .yui-overlay,.yui-panel-container {
2246 .yui-overlay,.yui-panel-container {
2247 visibility: hidden;
2247 visibility: hidden;
2248 position: absolute;
2248 position: absolute;
2249 z-index: 2;
2249 z-index: 2;
2250 }
2250 }
2251
2251
2252 .yui-tt {
2252 .yui-tt {
2253 visibility: hidden;
2253 visibility: hidden;
2254 position: absolute;
2254 position: absolute;
2255 color: #666;
2255 color: #666;
2256 background-color: #FFF;
2256 background-color: #FFF;
2257 border: 2px solid #003367;
2257 border: 2px solid #003367;
2258 font: 100% sans-serif;
2258 font: 100% sans-serif;
2259 width: auto;
2259 width: auto;
2260 opacity: 1px;
2260 opacity: 1px;
2261 padding: 8px;
2261 padding: 8px;
2262 white-space: pre-wrap;
2262 white-space: pre-wrap;
2263 -webkit-border-radius: 8px 8px 8px 8px;
2263 -webkit-border-radius: 8px 8px 8px 8px;
2264 -khtml-border-radius: 8px 8px 8px 8px;
2264 -khtml-border-radius: 8px 8px 8px 8px;
2265 -moz-border-radius: 8px 8px 8px 8px;
2265 -moz-border-radius: 8px 8px 8px 8px;
2266 border-radius: 8px 8px 8px 8px;
2266 border-radius: 8px 8px 8px 8px;
2267 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2267 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2268 }
2268 }
2269
2269
2270 .ac {
2270 .ac {
2271 vertical-align: top;
2271 vertical-align: top;
2272 }
2272 }
2273
2273
2274 .ac .yui-ac {
2274 .ac .yui-ac {
2275 position: relative;
2275 position: relative;
2276 font-size: 100%;
2276 font-size: 100%;
2277 }
2277 }
2278
2278
2279 .ac .perm_ac {
2279 .ac .perm_ac {
2280 width: 15em;
2280 width: 15em;
2281 }
2281 }
2282
2282
2283 .ac .yui-ac-input {
2283 .ac .yui-ac-input {
2284 width: 100%;
2284 width: 100%;
2285 }
2285 }
2286
2286
2287 .ac .yui-ac-container {
2287 .ac .yui-ac-container {
2288 position: absolute;
2288 position: absolute;
2289 top: 1.6em;
2289 top: 1.6em;
2290 width: 100%;
2290 width: 100%;
2291 }
2291 }
2292
2292
2293 .ac .yui-ac-content {
2293 .ac .yui-ac-content {
2294 position: absolute;
2294 position: absolute;
2295 width: 100%;
2295 width: 100%;
2296 border: 1px solid gray;
2296 border: 1px solid gray;
2297 background: #fff;
2297 background: #fff;
2298 overflow: hidden;
2298 overflow: hidden;
2299 z-index: 9050;
2299 z-index: 9050;
2300 }
2300 }
2301
2301
2302 .ac .yui-ac-shadow {
2302 .ac .yui-ac-shadow {
2303 position: absolute;
2303 position: absolute;
2304 width: 100%;
2304 width: 100%;
2305 background: #000;
2305 background: #000;
2306 -moz-opacity: 0.1px;
2306 -moz-opacity: 0.1px;
2307 opacity: .10;
2307 opacity: .10;
2308 filter: alpha(opacity = 10);
2308 filter: alpha(opacity = 10);
2309 z-index: 9049;
2309 z-index: 9049;
2310 margin: .3em;
2310 margin: .3em;
2311 }
2311 }
2312
2312
2313 .ac .yui-ac-content ul {
2313 .ac .yui-ac-content ul {
2314 width: 100%;
2314 width: 100%;
2315 margin: 0;
2315 margin: 0;
2316 padding: 0;
2316 padding: 0;
2317 }
2317 }
2318
2318
2319 .ac .yui-ac-content li {
2319 .ac .yui-ac-content li {
2320 cursor: default;
2320 cursor: default;
2321 white-space: nowrap;
2321 white-space: nowrap;
2322 margin: 0;
2322 margin: 0;
2323 padding: 2px 5px;
2323 padding: 2px 5px;
2324 }
2324 }
2325
2325
2326 .ac .yui-ac-content li.yui-ac-prehighlight {
2326 .ac .yui-ac-content li.yui-ac-prehighlight {
2327 background: #B3D4FF;
2327 background: #B3D4FF;
2328 }
2328 }
2329
2329
2330 .ac .yui-ac-content li.yui-ac-highlight {
2330 .ac .yui-ac-content li.yui-ac-highlight {
2331 background: #556CB5;
2331 background: #556CB5;
2332 color: #FFF;
2332 color: #FFF;
2333 }
2333 }
2334
2334
2335 .follow {
2335 .follow {
2336 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
2336 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
2337 height: 16px;
2337 height: 16px;
2338 width: 20px;
2338 width: 20px;
2339 cursor: pointer;
2339 cursor: pointer;
2340 display: block;
2340 display: block;
2341 float: right;
2341 float: right;
2342 margin-top: 2px;
2342 margin-top: 2px;
2343 }
2343 }
2344
2344
2345 .following {
2345 .following {
2346 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2346 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2347 height: 16px;
2347 height: 16px;
2348 width: 20px;
2348 width: 20px;
2349 cursor: pointer;
2349 cursor: pointer;
2350 display: block;
2350 display: block;
2351 float: right;
2351 float: right;
2352 margin-top: 2px;
2352 margin-top: 2px;
2353 }
2353 }
2354
2354
2355 .currently_following {
2355 .currently_following {
2356 padding-left: 10px;
2356 padding-left: 10px;
2357 padding-bottom: 5px;
2357 padding-bottom: 5px;
2358 }
2358 }
2359
2359
2360 .add_icon {
2360 .add_icon {
2361 background: url("../images/icons/add.png") no-repeat scroll 3px;
2361 background: url("../images/icons/add.png") no-repeat scroll 3px;
2362 padding-left: 20px;
2362 padding-left: 20px;
2363 padding-top: 0px;
2363 padding-top: 0px;
2364 text-align: left;
2364 text-align: left;
2365 }
2365 }
2366
2366
2367 .edit_icon {
2367 .edit_icon {
2368 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
2368 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
2369 padding-left: 20px;
2369 padding-left: 20px;
2370 padding-top: 0px;
2370 padding-top: 0px;
2371 text-align: left;
2371 text-align: left;
2372 }
2372 }
2373
2373
2374 .delete_icon {
2374 .delete_icon {
2375 background: url("../images/icons/delete.png") no-repeat scroll 3px;
2375 background: url("../images/icons/delete.png") no-repeat scroll 3px;
2376 padding-left: 20px;
2376 padding-left: 20px;
2377 padding-top: 0px;
2377 padding-top: 0px;
2378 text-align: left;
2378 text-align: left;
2379 }
2379 }
2380
2380
2381 .refresh_icon {
2381 .refresh_icon {
2382 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
2382 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
2383 3px;
2383 3px;
2384 padding-left: 20px;
2384 padding-left: 20px;
2385 padding-top: 0px;
2385 padding-top: 0px;
2386 text-align: left;
2386 text-align: left;
2387 }
2387 }
2388
2388
2389 .pull_icon {
2389 .pull_icon {
2390 background: url("../images/icons/connect.png") no-repeat scroll 3px;
2390 background: url("../images/icons/connect.png") no-repeat scroll 3px;
2391 padding-left: 20px;
2391 padding-left: 20px;
2392 padding-top: 0px;
2392 padding-top: 0px;
2393 text-align: left;
2393 text-align: left;
2394 }
2394 }
2395
2395
2396 .rss_icon {
2396 .rss_icon {
2397 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
2397 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
2398 padding-left: 20px;
2398 padding-left: 20px;
2399 padding-top: 0px;
2399 padding-top: 0px;
2400 text-align: left;
2400 text-align: left;
2401 }
2401 }
2402
2402
2403 .atom_icon {
2403 .atom_icon {
2404 background: url("../images/icons/atom.png") no-repeat scroll 3px;
2404 background: url("../images/icons/atom.png") no-repeat scroll 3px;
2405 padding-left: 20px;
2405 padding-left: 20px;
2406 padding-top: 0px;
2406 padding-top: 0px;
2407 text-align: left;
2407 text-align: left;
2408 }
2408 }
2409
2409
2410 .archive_icon {
2410 .archive_icon {
2411 background: url("../images/icons/compress.png") no-repeat scroll 3px;
2411 background: url("../images/icons/compress.png") no-repeat scroll 3px;
2412 padding-left: 20px;
2412 padding-left: 20px;
2413 text-align: left;
2413 text-align: left;
2414 padding-top: 1px;
2414 padding-top: 1px;
2415 }
2415 }
2416
2416
2417 .start_following_icon {
2417 .start_following_icon {
2418 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
2418 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
2419 padding-left: 20px;
2419 padding-left: 20px;
2420 text-align: left;
2420 text-align: left;
2421 padding-top: 0px;
2421 padding-top: 0px;
2422 }
2422 }
2423
2423
2424 .stop_following_icon {
2424 .stop_following_icon {
2425 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2425 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2426 padding-left: 20px;
2426 padding-left: 20px;
2427 text-align: left;
2427 text-align: left;
2428 padding-top: 0px;
2428 padding-top: 0px;
2429 }
2429 }
2430
2430
2431 .action_button {
2431 .action_button {
2432 border: 0;
2432 border: 0;
2433 display: inline;
2433 display: inline;
2434 }
2434 }
2435
2435
2436 .action_button:hover {
2436 .action_button:hover {
2437 border: 0;
2437 border: 0;
2438 text-decoration: underline;
2438 text-decoration: underline;
2439 cursor: pointer;
2439 cursor: pointer;
2440 }
2440 }
2441
2441
2442 #switch_repos {
2442 #switch_repos {
2443 position: absolute;
2443 position: absolute;
2444 height: 25px;
2444 height: 25px;
2445 z-index: 1;
2445 z-index: 1;
2446 }
2446 }
2447
2447
2448 #switch_repos select {
2448 #switch_repos select {
2449 min-width: 150px;
2449 min-width: 150px;
2450 max-height: 250px;
2450 max-height: 250px;
2451 z-index: 1;
2451 z-index: 1;
2452 }
2452 }
2453
2453
2454 .breadcrumbs {
2454 .breadcrumbs {
2455 border: medium none;
2455 border: medium none;
2456 color: #FFF;
2456 color: #FFF;
2457 float: left;
2457 float: left;
2458 text-transform: uppercase;
2458 text-transform: uppercase;
2459 font-weight: 700;
2459 font-weight: 700;
2460 font-size: 14px;
2460 font-size: 14px;
2461 margin: 0;
2461 margin: 0;
2462 padding: 11px 0 11px 10px;
2462 padding: 11px 0 11px 10px;
2463 }
2463 }
2464
2464
2465 .breadcrumbs a {
2465 .breadcrumbs a {
2466 color: #FFF;
2466 color: #FFF;
2467 }
2467 }
2468
2468
2469 .flash_msg {
2469 .flash_msg {
2470
2470
2471 }
2471 }
2472
2472
2473 .flash_msg ul {
2473 .flash_msg ul {
2474
2474
2475 }
2475 }
2476
2476
2477 .error_msg {
2477 .error_msg {
2478 background-color: #c43c35;
2478 background-color: #c43c35;
2479 background-repeat: repeat-x;
2479 background-repeat: repeat-x;
2480 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b),
2480 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b),
2481 to(#c43c35) );
2481 to(#c43c35) );
2482 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
2482 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
2483 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
2483 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
2484 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b),
2484 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b),
2485 color-stop(100%, #c43c35) );
2485 color-stop(100%, #c43c35) );
2486 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
2486 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
2487 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
2487 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
2488 background-image: linear-gradient(top, #ee5f5b, #c43c35);
2488 background-image: linear-gradient(top, #ee5f5b, #c43c35);
2489 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',
2489 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',
2490 endColorstr='#c43c35', GradientType=0 );
2490 endColorstr='#c43c35', GradientType=0 );
2491 border-color: #c43c35 #c43c35 #882a25;
2491 border-color: #c43c35 #c43c35 #882a25;
2492 }
2492 }
2493
2493
2494 .warning_msg {
2494 .warning_msg {
2495 color: #404040 !important;
2495 color: #404040 !important;
2496 background-color: #eedc94;
2496 background-color: #eedc94;
2497 background-repeat: repeat-x;
2497 background-repeat: repeat-x;
2498 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
2498 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
2499 to(#eedc94) );
2499 to(#eedc94) );
2500 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
2500 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
2501 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
2501 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
2502 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1),
2502 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1),
2503 color-stop(100%, #eedc94) );
2503 color-stop(100%, #eedc94) );
2504 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
2504 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
2505 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
2505 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
2506 background-image: linear-gradient(top, #fceec1, #eedc94);
2506 background-image: linear-gradient(top, #fceec1, #eedc94);
2507 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1',
2507 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1',
2508 endColorstr='#eedc94', GradientType=0 );
2508 endColorstr='#eedc94', GradientType=0 );
2509 border-color: #eedc94 #eedc94 #e4c652;
2509 border-color: #eedc94 #eedc94 #e4c652;
2510 }
2510 }
2511
2511
2512 .success_msg {
2512 .success_msg {
2513 background-color: #57a957;
2513 background-color: #57a957;
2514 background-repeat: repeat-x !important;
2514 background-repeat: repeat-x !important;
2515 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462),
2515 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462),
2516 to(#57a957) );
2516 to(#57a957) );
2517 background-image: -moz-linear-gradient(top, #62c462, #57a957);
2517 background-image: -moz-linear-gradient(top, #62c462, #57a957);
2518 background-image: -ms-linear-gradient(top, #62c462, #57a957);
2518 background-image: -ms-linear-gradient(top, #62c462, #57a957);
2519 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462),
2519 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462),
2520 color-stop(100%, #57a957) );
2520 color-stop(100%, #57a957) );
2521 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
2521 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
2522 background-image: -o-linear-gradient(top, #62c462, #57a957);
2522 background-image: -o-linear-gradient(top, #62c462, #57a957);
2523 background-image: linear-gradient(top, #62c462, #57a957);
2523 background-image: linear-gradient(top, #62c462, #57a957);
2524 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462',
2524 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462',
2525 endColorstr='#57a957', GradientType=0 );
2525 endColorstr='#57a957', GradientType=0 );
2526 border-color: #57a957 #57a957 #3d773d;
2526 border-color: #57a957 #57a957 #3d773d;
2527 }
2527 }
2528
2528
2529 .notice_msg {
2529 .notice_msg {
2530 background-color: #339bb9;
2530 background-color: #339bb9;
2531 background-repeat: repeat-x;
2531 background-repeat: repeat-x;
2532 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de),
2532 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de),
2533 to(#339bb9) );
2533 to(#339bb9) );
2534 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
2534 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
2535 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
2535 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
2536 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de),
2536 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de),
2537 color-stop(100%, #339bb9) );
2537 color-stop(100%, #339bb9) );
2538 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
2538 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
2539 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
2539 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
2540 background-image: linear-gradient(top, #5bc0de, #339bb9);
2540 background-image: linear-gradient(top, #5bc0de, #339bb9);
2541 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de',
2541 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de',
2542 endColorstr='#339bb9', GradientType=0 );
2542 endColorstr='#339bb9', GradientType=0 );
2543 border-color: #339bb9 #339bb9 #22697d;
2543 border-color: #339bb9 #339bb9 #22697d;
2544 }
2544 }
2545
2545
2546 .success_msg,.error_msg,.notice_msg,.warning_msg {
2546 .success_msg,.error_msg,.notice_msg,.warning_msg {
2547 font-size: 12px;
2547 font-size: 12px;
2548 font-weight: 700;
2548 font-weight: 700;
2549 min-height: 14px;
2549 min-height: 14px;
2550 line-height: 14px;
2550 line-height: 14px;
2551 margin-bottom: 10px;
2551 margin-bottom: 10px;
2552 margin-top: 0;
2552 margin-top: 0;
2553 display: block;
2553 display: block;
2554 overflow: auto;
2554 overflow: auto;
2555 padding: 6px 10px 6px 10px;
2555 padding: 6px 10px 6px 10px;
2556 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2556 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2557 position: relative;
2557 position: relative;
2558 color: #FFF;
2558 color: #FFF;
2559 border-width: 1px;
2559 border-width: 1px;
2560 border-style: solid;
2560 border-style: solid;
2561 -webkit-border-radius: 4px;
2561 -webkit-border-radius: 4px;
2562 -moz-border-radius: 4px;
2562 -moz-border-radius: 4px;
2563 border-radius: 4px;
2563 border-radius: 4px;
2564 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2564 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2565 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2565 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2566 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2566 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2567 }
2567 }
2568
2568
2569 #msg_close {
2569 #msg_close {
2570 background: transparent url("../icons/cross_grey_small.png") no-repeat
2570 background: transparent url("../icons/cross_grey_small.png") no-repeat
2571 scroll 0 0;
2571 scroll 0 0;
2572 cursor: pointer;
2572 cursor: pointer;
2573 height: 16px;
2573 height: 16px;
2574 position: absolute;
2574 position: absolute;
2575 right: 5px;
2575 right: 5px;
2576 top: 5px;
2576 top: 5px;
2577 width: 16px;
2577 width: 16px;
2578 }
2578 }
2579
2579
2580 div#legend_container table,div#legend_choices table {
2580 div#legend_container table,div#legend_choices table {
2581 width: auto !important;
2581 width: auto !important;
2582 }
2582 }
2583
2583
2584 table#permissions_manage {
2584 table#permissions_manage {
2585 width: 0 !important;
2585 width: 0 !important;
2586 }
2586 }
2587
2587
2588 table#permissions_manage span.private_repo_msg {
2588 table#permissions_manage span.private_repo_msg {
2589 font-size: 0.8em;
2589 font-size: 0.8em;
2590 opacity: 0.6px;
2590 opacity: 0.6px;
2591 }
2591 }
2592
2592
2593 table#permissions_manage td.private_repo_msg {
2593 table#permissions_manage td.private_repo_msg {
2594 font-size: 0.8em;
2594 font-size: 0.8em;
2595 }
2595 }
2596
2596
2597 table#permissions_manage tr#add_perm_input td {
2597 table#permissions_manage tr#add_perm_input td {
2598 vertical-align: middle;
2598 vertical-align: middle;
2599 }
2599 }
2600
2600
2601 div.gravatar {
2601 div.gravatar {
2602 background-color: #FFF;
2602 background-color: #FFF;
2603 border: 0px solid #D0D0D0;
2603 border: 0px solid #D0D0D0;
2604 float: left;
2604 float: left;
2605 margin-right: 0.7em;
2605 margin-right: 0.7em;
2606 padding: 2px 2px 0;
2606 padding: 2px 2px 2px 2px;
2607 line-height:0;
2607 -webkit-border-radius: 6px;
2608 -webkit-border-radius: 6px;
2608 -khtml-border-radius: 6px;
2609 -khtml-border-radius: 6px;
2609 -moz-border-radius: 6px;
2610 -moz-border-radius: 6px;
2610 border-radius: 6px;
2611 border-radius: 6px;
2611 }
2612 }
2612
2613
2613 div.gravatar img {
2614 div.gravatar img {
2614 -webkit-border-radius: 4px;
2615 -webkit-border-radius: 4px;
2615 -khtml-border-radius: 4px;
2616 -khtml-border-radius: 4px;
2616 -moz-border-radius: 4px;
2617 -moz-border-radius: 4px;
2617 border-radius: 4px;
2618 border-radius: 4px;
2618 }
2619 }
2619
2620
2620 #header,#content,#footer {
2621 #header,#content,#footer {
2621 min-width: 978px;
2622 min-width: 978px;
2622 }
2623 }
2623
2624
2624 #content {
2625 #content {
2625 clear: both;
2626 clear: both;
2626 overflow: hidden;
2627 overflow: hidden;
2627 padding: 14px 10px;
2628 padding: 14px 10px;
2628 }
2629 }
2629
2630
2630 #content div.box div.title div.search {
2631 #content div.box div.title div.search {
2631
2632
2632 border-left: 1px solid #316293;
2633 border-left: 1px solid #316293;
2633 }
2634 }
2634
2635
2635 #content div.box div.title div.search div.input input {
2636 #content div.box div.title div.search div.input input {
2636 border: 1px solid #316293;
2637 border: 1px solid #316293;
2637 }
2638 }
2638
2639
2639 .ui-button-small a:hover {
2640 .ui-button-small a:hover {
2640
2641
2641 }
2642 }
2642
2643
2643 input.ui-button-small,.ui-button-small {
2644 input.ui-button-small,.ui-button-small {
2644 background: #e5e3e3 url("../images/button.png") repeat-x !important;
2645 background: #e5e3e3 url("../images/button.png") repeat-x !important;
2645 border-top: 1px solid #DDD !important;
2646 border-top: 1px solid #DDD !important;
2646 border-left: 1px solid #c6c6c6 !important;
2647 border-left: 1px solid #c6c6c6 !important;
2647 border-right: 1px solid #DDD !important;
2648 border-right: 1px solid #DDD !important;
2648 border-bottom: 1px solid #c6c6c6 !important;
2649 border-bottom: 1px solid #c6c6c6 !important;
2649 color: #515151 !important;
2650 color: #515151 !important;
2650 outline: none !important;
2651 outline: none !important;
2651 margin: 0 !important;
2652 margin: 0 !important;
2652 -webkit-border-radius: 4px 4px 4px 4px !important;
2653 -webkit-border-radius: 4px 4px 4px 4px !important;
2653 -khtml-border-radius: 4px 4px 4px 4px !important;
2654 -khtml-border-radius: 4px 4px 4px 4px !important;
2654 -moz-border-radius: 4px 4px 4px 4px !important;
2655 -moz-border-radius: 4px 4px 4px 4px !important;
2655 border-radius: 4px 4px 4px 4px !important;
2656 border-radius: 4px 4px 4px 4px !important;
2656 box-shadow: 0 1px 0 #ececec !important;
2657 box-shadow: 0 1px 0 #ececec !important;
2657 cursor: pointer !important;
2658 cursor: pointer !important;
2658 padding: 3px 3px 3px 3px;
2659 padding: 3px 3px 3px 3px;
2659 }
2660 }
2660
2661
2661 input.ui-button-small.xsmall,.ui-button-small.xsmall{
2662 input.ui-button-small.xsmall,.ui-button-small.xsmall{
2662 padding: 1px 2px 1px 1px;
2663 padding: 1px 2px 1px 1px;
2663 }
2664 }
2664
2665
2665 input.ui-button-small:hover,.ui-button-small:hover {
2666 input.ui-button-small:hover,.ui-button-small:hover {
2666 background: #b4b4b4 url("../images/button_selected.png") repeat-x
2667 background: #b4b4b4 url("../images/button_selected.png") repeat-x
2667 !important;
2668 !important;
2668 border-top: 1px solid #ccc !important;
2669 border-top: 1px solid #ccc !important;
2669 border-left: 1px solid #bebebe !important;
2670 border-left: 1px solid #bebebe !important;
2670 border-right: 1px solid #b1b1b1 !important;
2671 border-right: 1px solid #b1b1b1 !important;
2671 border-bottom: 1px solid #afafaf !important;
2672 border-bottom: 1px solid #afafaf !important;
2672 text-decoration: none;
2673 text-decoration: none;
2673 }
2674 }
2674
2675
2675 input.ui-button-small-blue,.ui-button-small-blue {
2676 input.ui-button-small-blue,.ui-button-small-blue {
2676 background: #4e85bb url("../images/button_highlight.png") repeat-x;
2677 background: #4e85bb url("../images/button_highlight.png") repeat-x;
2677 border-top: 1px solid #5c91a4;
2678 border-top: 1px solid #5c91a4;
2678 border-left: 1px solid #2a6f89;
2679 border-left: 1px solid #2a6f89;
2679 border-right: 1px solid #2b7089;
2680 border-right: 1px solid #2b7089;
2680 border-bottom: 1px solid #1a6480;
2681 border-bottom: 1px solid #1a6480;
2681 color: #fff;
2682 color: #fff;
2682 -webkit-border-radius: 4px 4px 4px 4px;
2683 -webkit-border-radius: 4px 4px 4px 4px;
2683 -khtml-border-radius: 4px 4px 4px 4px;
2684 -khtml-border-radius: 4px 4px 4px 4px;
2684 -moz-border-radius: 4px 4px 4px 4px;
2685 -moz-border-radius: 4px 4px 4px 4px;
2685 border-radius: 4px 4px 4px 4px;
2686 border-radius: 4px 4px 4px 4px;
2686 box-shadow: 0 1px 0 #ececec;
2687 box-shadow: 0 1px 0 #ececec;
2687 cursor: pointer;
2688 cursor: pointer;
2688 padding: 0px 2px 1px 2px;
2689 padding: 0px 2px 1px 2px;
2689 }
2690 }
2690
2691
2691 input.ui-button-small-blue:hover {
2692 input.ui-button-small-blue:hover {
2692
2693
2693 }
2694 }
2694
2695
2695 ins,div.options a:hover {
2696 ins,div.options a:hover {
2696 text-decoration: none;
2697 text-decoration: none;
2697 }
2698 }
2698
2699
2699 img,#header #header-inner #quick li a:hover span.normal,#header #header-inner #quick li ul li.last,#content div.box div.form div.fields div.field div.textarea table td table td a,#clone_url
2700 img,#header #header-inner #quick li a:hover span.normal,#header #header-inner #quick li ul li.last,#content div.box div.form div.fields div.field div.textarea table td table td a,#clone_url
2700 {
2701 {
2701 border: none;
2702 border: none;
2702 }
2703 }
2703
2704
2704 img.icon,.right .merge img {
2705 img.icon,.right .merge img {
2705 vertical-align: bottom;
2706 vertical-align: bottom;
2706 }
2707 }
2707
2708
2708 #header ul#logged-user,#content div.box div.title ul.links,#content div.box div.message div.dismiss,#content div.box div.traffic div.legend ul
2709 #header ul#logged-user,#content div.box div.title ul.links,#content div.box div.message div.dismiss,#content div.box div.traffic div.legend ul
2709 {
2710 {
2710 float: right;
2711 float: right;
2711 margin: 0;
2712 margin: 0;
2712 padding: 0;
2713 padding: 0;
2713 }
2714 }
2714
2715
2715 #header #header-inner #home,#header #header-inner #logo,#content div.box ul.left,#content div.box ol.left,#content div.box div.pagination-left,div#commit_history,div#legend_data,div#legend_container,div#legend_choices
2716 #header #header-inner #home,#header #header-inner #logo,#content div.box ul.left,#content div.box ol.left,#content div.box div.pagination-left,div#commit_history,div#legend_data,div#legend_container,div#legend_choices
2716 {
2717 {
2717 float: left;
2718 float: left;
2718 }
2719 }
2719
2720
2720 #header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
2721 #header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
2721 {
2722 {
2722 display: none;
2723 display: none;
2723 }
2724 }
2724
2725
2725 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded
2726 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded
2726 {
2727 {
2727 display: block;
2728 display: block;
2728 }
2729 }
2729
2730
2730 #content div.graph {
2731 #content div.graph {
2731 padding: 0 10px 10px;
2732 padding: 0 10px 10px;
2732 }
2733 }
2733
2734
2734 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
2735 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
2735 {
2736 {
2736 color: #bfe3ff;
2737 color: #bfe3ff;
2737 }
2738 }
2738
2739
2739 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal
2740 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal
2740 {
2741 {
2741 margin: 10px 24px 10px 44px;
2742 margin: 10px 24px 10px 44px;
2742 }
2743 }
2743
2744
2744 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
2745 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
2745 {
2746 {
2746 clear: both;
2747 clear: both;
2747 overflow: hidden;
2748 overflow: hidden;
2748 margin: 0;
2749 margin: 0;
2749 padding: 0 20px 10px;
2750 padding: 0 20px 10px;
2750 }
2751 }
2751
2752
2752 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
2753 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
2753 {
2754 {
2754 clear: both;
2755 clear: both;
2755 overflow: hidden;
2756 overflow: hidden;
2756 margin: 0;
2757 margin: 0;
2757 padding: 0;
2758 padding: 0;
2758 }
2759 }
2759
2760
2760 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span
2761 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span
2761 {
2762 {
2762 height: 1%;
2763 height: 1%;
2763 display: block;
2764 display: block;
2764 color: #363636;
2765 color: #363636;
2765 margin: 0;
2766 margin: 0;
2766 padding: 2px 0 0;
2767 padding: 2px 0 0;
2767 }
2768 }
2768
2769
2769 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error
2770 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error
2770 {
2771 {
2771 background: #FBE3E4;
2772 background: #FBE3E4;
2772 border-top: 1px solid #e1b2b3;
2773 border-top: 1px solid #e1b2b3;
2773 border-left: 1px solid #e1b2b3;
2774 border-left: 1px solid #e1b2b3;
2774 border-right: 1px solid #FBC2C4;
2775 border-right: 1px solid #FBC2C4;
2775 border-bottom: 1px solid #FBC2C4;
2776 border-bottom: 1px solid #FBC2C4;
2776 }
2777 }
2777
2778
2778 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success
2779 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success
2779 {
2780 {
2780 background: #E6EFC2;
2781 background: #E6EFC2;
2781 border-top: 1px solid #cebb98;
2782 border-top: 1px solid #cebb98;
2782 border-left: 1px solid #cebb98;
2783 border-left: 1px solid #cebb98;
2783 border-right: 1px solid #c6d880;
2784 border-right: 1px solid #c6d880;
2784 border-bottom: 1px solid #c6d880;
2785 border-bottom: 1px solid #c6d880;
2785 }
2786 }
2786
2787
2787 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input
2788 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input
2788 {
2789 {
2789 margin: 0;
2790 margin: 0;
2790 }
2791 }
2791
2792
2792 #content div.box-left div.form div.fields div.field div.select,#content div.box-left div.form div.fields div.field div.checkboxes,#content div.box-left div.form div.fields div.field div.radios,#content div.box-right div.form div.fields div.field div.select,#content div.box-right div.form div.fields div.field div.checkboxes,#content div.box-right div.form div.fields div.field div.radios
2793 #content div.box-left div.form div.fields div.field div.select,#content div.box-left div.form div.fields div.field div.checkboxes,#content div.box-left div.form div.fields div.field div.radios,#content div.box-right div.form div.fields div.field div.select,#content div.box-right div.form div.fields div.field div.checkboxes,#content div.box-right div.form div.fields div.field div.radios
2793 {
2794 {
2794 margin: 0 0 0 0px !important;
2795 margin: 0 0 0 0px !important;
2795 padding: 0;
2796 padding: 0;
2796 }
2797 }
2797
2798
2798 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios
2799 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios
2799 {
2800 {
2800 margin: 0 0 0 200px;
2801 margin: 0 0 0 200px;
2801 padding: 0;
2802 padding: 0;
2802 }
2803 }
2803
2804
2804 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover
2805 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover
2805 {
2806 {
2806 color: #000;
2807 color: #000;
2807 text-decoration: none;
2808 text-decoration: none;
2808 }
2809 }
2809
2810
2810 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
2811 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
2811 {
2812 {
2812 border: 1px solid #666;
2813 border: 1px solid #666;
2813 }
2814 }
2814
2815
2815 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio
2816 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio
2816 {
2817 {
2817 clear: both;
2818 clear: both;
2818 overflow: hidden;
2819 overflow: hidden;
2819 margin: 0;
2820 margin: 0;
2820 padding: 8px 0 2px;
2821 padding: 8px 0 2px;
2821 }
2822 }
2822
2823
2823 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input
2824 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input
2824 {
2825 {
2825 float: left;
2826 float: left;
2826 margin: 0;
2827 margin: 0;
2827 }
2828 }
2828
2829
2829 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label
2830 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label
2830 {
2831 {
2831 height: 1%;
2832 height: 1%;
2832 display: block;
2833 display: block;
2833 float: left;
2834 float: left;
2834 margin: 2px 0 0 4px;
2835 margin: 2px 0 0 4px;
2835 }
2836 }
2836
2837
2837 div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input
2838 div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input
2838 {
2839 {
2839 color: #000;
2840 color: #000;
2840 font-size: 11px;
2841 font-size: 11px;
2841 font-weight: 700;
2842 font-weight: 700;
2842 margin: 0;
2843 margin: 0;
2843 }
2844 }
2844
2845
2845 input.ui-button {
2846 input.ui-button {
2846 background: #e5e3e3 url("../images/button.png") repeat-x;
2847 background: #e5e3e3 url("../images/button.png") repeat-x;
2847 border-top: 1px solid #DDD;
2848 border-top: 1px solid #DDD;
2848 border-left: 1px solid #c6c6c6;
2849 border-left: 1px solid #c6c6c6;
2849 border-right: 1px solid #DDD;
2850 border-right: 1px solid #DDD;
2850 border-bottom: 1px solid #c6c6c6;
2851 border-bottom: 1px solid #c6c6c6;
2851 color: #515151 !important;
2852 color: #515151 !important;
2852 outline: none;
2853 outline: none;
2853 margin: 0;
2854 margin: 0;
2854 padding: 6px 12px;
2855 padding: 6px 12px;
2855 -webkit-border-radius: 4px 4px 4px 4px;
2856 -webkit-border-radius: 4px 4px 4px 4px;
2856 -khtml-border-radius: 4px 4px 4px 4px;
2857 -khtml-border-radius: 4px 4px 4px 4px;
2857 -moz-border-radius: 4px 4px 4px 4px;
2858 -moz-border-radius: 4px 4px 4px 4px;
2858 border-radius: 4px 4px 4px 4px;
2859 border-radius: 4px 4px 4px 4px;
2859 box-shadow: 0 1px 0 #ececec;
2860 box-shadow: 0 1px 0 #ececec;
2860 cursor: pointer;
2861 cursor: pointer;
2861 }
2862 }
2862
2863
2863 input.ui-button:hover {
2864 input.ui-button:hover {
2864 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2865 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2865 border-top: 1px solid #ccc;
2866 border-top: 1px solid #ccc;
2866 border-left: 1px solid #bebebe;
2867 border-left: 1px solid #bebebe;
2867 border-right: 1px solid #b1b1b1;
2868 border-right: 1px solid #b1b1b1;
2868 border-bottom: 1px solid #afafaf;
2869 border-bottom: 1px solid #afafaf;
2869 }
2870 }
2870
2871
2871 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
2872 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
2872 {
2873 {
2873 display: inline;
2874 display: inline;
2874 }
2875 }
2875
2876
2876 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
2877 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
2877 {
2878 {
2878 margin: 10px 0 0 200px;
2879 margin: 10px 0 0 200px;
2879 padding: 0;
2880 padding: 0;
2880 }
2881 }
2881
2882
2882 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons
2883 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons
2883 {
2884 {
2884 margin: 10px 0 0;
2885 margin: 10px 0 0;
2885 }
2886 }
2886
2887
2887 #content div.box table td.user,#content div.box table td.address {
2888 #content div.box table td.user,#content div.box table td.address {
2888 width: 10%;
2889 width: 10%;
2889 text-align: center;
2890 text-align: center;
2890 }
2891 }
2891
2892
2892 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link
2893 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link
2893 {
2894 {
2894 text-align: right;
2895 text-align: right;
2895 margin: 6px 0 0;
2896 margin: 6px 0 0;
2896 padding: 0;
2897 padding: 0;
2897 }
2898 }
2898
2899
2899 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover
2900 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover
2900 {
2901 {
2901 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2902 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2902 border-top: 1px solid #ccc;
2903 border-top: 1px solid #ccc;
2903 border-left: 1px solid #bebebe;
2904 border-left: 1px solid #bebebe;
2904 border-right: 1px solid #b1b1b1;
2905 border-right: 1px solid #b1b1b1;
2905 border-bottom: 1px solid #afafaf;
2906 border-bottom: 1px solid #afafaf;
2906 color: #515151;
2907 color: #515151;
2907 margin: 0;
2908 margin: 0;
2908 padding: 6px 12px;
2909 padding: 6px 12px;
2909 }
2910 }
2910
2911
2911 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
2912 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
2912 {
2913 {
2913 text-align: left;
2914 text-align: left;
2914 float: left;
2915 float: left;
2915 margin: 0;
2916 margin: 0;
2916 padding: 0;
2917 padding: 0;
2917 }
2918 }
2918
2919
2919 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
2920 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
2920 {
2921 {
2921 height: 1%;
2922 height: 1%;
2922 display: block;
2923 display: block;
2923 float: left;
2924 float: left;
2924 background: #ebebeb url("../images/pager.png") repeat-x;
2925 background: #ebebeb url("../images/pager.png") repeat-x;
2925 border-top: 1px solid #dedede;
2926 border-top: 1px solid #dedede;
2926 border-left: 1px solid #cfcfcf;
2927 border-left: 1px solid #cfcfcf;
2927 border-right: 1px solid #c4c4c4;
2928 border-right: 1px solid #c4c4c4;
2928 border-bottom: 1px solid #c4c4c4;
2929 border-bottom: 1px solid #c4c4c4;
2929 color: #4A4A4A;
2930 color: #4A4A4A;
2930 font-weight: 700;
2931 font-weight: 700;
2931 margin: 0;
2932 margin: 0;
2932 padding: 6px 8px;
2933 padding: 6px 8px;
2933 }
2934 }
2934
2935
2935 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
2936 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
2936 {
2937 {
2937 color: #B4B4B4;
2938 color: #B4B4B4;
2938 padding: 6px;
2939 padding: 6px;
2939 }
2940 }
2940
2941
2941 #login,#register {
2942 #login,#register {
2942 width: 520px;
2943 width: 520px;
2943 margin: 10% auto 0;
2944 margin: 10% auto 0;
2944 padding: 0;
2945 padding: 0;
2945 }
2946 }
2946
2947
2947 #login div.color,#register div.color {
2948 #login div.color,#register div.color {
2948 clear: both;
2949 clear: both;
2949 overflow: hidden;
2950 overflow: hidden;
2950 background: #FFF;
2951 background: #FFF;
2951 margin: 10px auto 0;
2952 margin: 10px auto 0;
2952 padding: 3px 3px 3px 0;
2953 padding: 3px 3px 3px 0;
2953 }
2954 }
2954
2955
2955 #login div.color a,#register div.color a {
2956 #login div.color a,#register div.color a {
2956 width: 20px;
2957 width: 20px;
2957 height: 20px;
2958 height: 20px;
2958 display: block;
2959 display: block;
2959 float: left;
2960 float: left;
2960 margin: 0 0 0 3px;
2961 margin: 0 0 0 3px;
2961 padding: 0;
2962 padding: 0;
2962 }
2963 }
2963
2964
2964 #login div.title h5,#register div.title h5 {
2965 #login div.title h5,#register div.title h5 {
2965 color: #fff;
2966 color: #fff;
2966 margin: 10px;
2967 margin: 10px;
2967 padding: 0;
2968 padding: 0;
2968 }
2969 }
2969
2970
2970 #login div.form div.fields div.field,#register div.form div.fields div.field
2971 #login div.form div.fields div.field,#register div.form div.fields div.field
2971 {
2972 {
2972 clear: both;
2973 clear: both;
2973 overflow: hidden;
2974 overflow: hidden;
2974 margin: 0;
2975 margin: 0;
2975 padding: 0 0 10px;
2976 padding: 0 0 10px;
2976 }
2977 }
2977
2978
2978 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
2979 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
2979 {
2980 {
2980 height: 1%;
2981 height: 1%;
2981 display: block;
2982 display: block;
2982 color: red;
2983 color: red;
2983 margin: 8px 0 0;
2984 margin: 8px 0 0;
2984 padding: 0;
2985 padding: 0;
2985 max-width: 320px;
2986 max-width: 320px;
2986 }
2987 }
2987
2988
2988 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
2989 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
2989 {
2990 {
2990 color: #000;
2991 color: #000;
2991 font-weight: 700;
2992 font-weight: 700;
2992 }
2993 }
2993
2994
2994 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
2995 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
2995 {
2996 {
2996 float: left;
2997 float: left;
2997 margin: 0;
2998 margin: 0;
2998 padding: 0;
2999 padding: 0;
2999 }
3000 }
3000
3001
3001 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3002 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3002 {
3003 {
3003 margin: 0 0 0 184px;
3004 margin: 0 0 0 184px;
3004 padding: 0;
3005 padding: 0;
3005 }
3006 }
3006
3007
3007 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3008 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3008 {
3009 {
3009 color: #565656;
3010 color: #565656;
3010 font-weight: 700;
3011 font-weight: 700;
3011 }
3012 }
3012
3013
3013 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3014 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3014 {
3015 {
3015 color: #000;
3016 color: #000;
3016 font-size: 1em;
3017 font-size: 1em;
3017 font-weight: 700;
3018 font-weight: 700;
3018 margin: 0;
3019 margin: 0;
3019 }
3020 }
3020
3021
3021 #changeset_content .container .wrapper,#graph_content .container .wrapper
3022 #changeset_content .container .wrapper,#graph_content .container .wrapper
3022 {
3023 {
3023 width: 600px;
3024 width: 600px;
3024 }
3025 }
3025
3026
3026 #changeset_content .container .left,#graph_content .container .left {
3027 #changeset_content .container .left,#graph_content .container .left {
3027 float: left;
3028 float: left;
3028 width: 70%;
3029 width: 70%;
3029 padding-left: 5px;
3030 padding-left: 5px;
3030 }
3031 }
3031
3032
3032 #changeset_content .container .left .date,.ac .match {
3033 #changeset_content .container .left .date,.ac .match {
3033 font-weight: 700;
3034 font-weight: 700;
3034 padding-top: 5px;
3035 padding-top: 5px;
3035 padding-bottom: 5px;
3036 padding-bottom: 5px;
3036 }
3037 }
3037
3038
3038 div#legend_container table td,div#legend_choices table td {
3039 div#legend_container table td,div#legend_choices table td {
3039 border: none !important;
3040 border: none !important;
3040 height: 20px !important;
3041 height: 20px !important;
3041 padding: 0 !important;
3042 padding: 0 !important;
3042 }
3043 }
3043
3044
3044 .q_filter_box {
3045 .q_filter_box {
3045 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3046 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3046 -webkit-border-radius: 4px;
3047 -webkit-border-radius: 4px;
3047 -moz-border-radius: 4px;
3048 -moz-border-radius: 4px;
3048 border-radius: 4px;
3049 border-radius: 4px;
3049 border: 0 none;
3050 border: 0 none;
3050 color: #AAAAAA;
3051 color: #AAAAAA;
3051 margin-bottom: -4px;
3052 margin-bottom: -4px;
3052 margin-top: -4px;
3053 margin-top: -4px;
3053 padding-left: 3px;
3054 padding-left: 3px;
3054 }
3055 }
3055
3056
3056 #node_filter {
3057 #node_filter {
3057 border: 0px solid #545454;
3058 border: 0px solid #545454;
3058 color: #AAAAAA;
3059 color: #AAAAAA;
3059 padding-left: 3px;
3060 padding-left: 3px;
3060 }
3061 }
3061
3062
3062 /*README STYLE*/
3063 /*README STYLE*/
3063
3064
3064 div.readme {
3065 div.readme {
3065 padding:0px;
3066 padding:0px;
3066 }
3067 }
3067
3068
3068 div.readme h2 {
3069 div.readme h2 {
3069 font-weight: normal;
3070 font-weight: normal;
3070 }
3071 }
3071
3072
3072 div.readme .readme_box {
3073 div.readme .readme_box {
3073 background-color: #fafafa;
3074 background-color: #fafafa;
3074 }
3075 }
3075
3076
3076 div.readme .readme_box {
3077 div.readme .readme_box {
3077 clear:both;
3078 clear:both;
3078 overflow:hidden;
3079 overflow:hidden;
3079 margin:0;
3080 margin:0;
3080 padding:0 20px 10px;
3081 padding:0 20px 10px;
3081 }
3082 }
3082
3083
3083 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
3084 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
3084 border-bottom: 0 !important;
3085 border-bottom: 0 !important;
3085 margin: 0 !important;
3086 margin: 0 !important;
3086 padding: 0 !important;
3087 padding: 0 !important;
3087 line-height: 1.5em !important;
3088 line-height: 1.5em !important;
3088 }
3089 }
3089
3090
3090
3091
3091 div.readme .readme_box h1:first-child {
3092 div.readme .readme_box h1:first-child {
3092 padding-top: .25em !important;
3093 padding-top: .25em !important;
3093 }
3094 }
3094
3095
3095 div.readme .readme_box h2, div.readme .readme_box h3 {
3096 div.readme .readme_box h2, div.readme .readme_box h3 {
3096 margin: 1em 0 !important;
3097 margin: 1em 0 !important;
3097 }
3098 }
3098
3099
3099 div.readme .readme_box h2 {
3100 div.readme .readme_box h2 {
3100 margin-top: 1.5em !important;
3101 margin-top: 1.5em !important;
3101 border-top: 4px solid #e0e0e0 !important;
3102 border-top: 4px solid #e0e0e0 !important;
3102 padding-top: .5em !important;
3103 padding-top: .5em !important;
3103 }
3104 }
3104
3105
3105 div.readme .readme_box p {
3106 div.readme .readme_box p {
3106 color: black !important;
3107 color: black !important;
3107 margin: 1em 0 !important;
3108 margin: 1em 0 !important;
3108 line-height: 1.5em !important;
3109 line-height: 1.5em !important;
3109 }
3110 }
3110
3111
3111 div.readme .readme_box ul {
3112 div.readme .readme_box ul {
3112 list-style: disc !important;
3113 list-style: disc !important;
3113 margin: 1em 0 1em 2em !important;
3114 margin: 1em 0 1em 2em !important;
3114 }
3115 }
3115
3116
3116 div.readme .readme_box ol {
3117 div.readme .readme_box ol {
3117 list-style: decimal;
3118 list-style: decimal;
3118 margin: 1em 0 1em 2em !important;
3119 margin: 1em 0 1em 2em !important;
3119 }
3120 }
3120
3121
3121 div.readme .readme_box pre, code {
3122 div.readme .readme_box pre, code {
3122 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3123 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3123 }
3124 }
3124
3125
3125 div.readme .readme_box code {
3126 div.readme .readme_box code {
3126 font-size: 12px !important;
3127 font-size: 12px !important;
3127 background-color: ghostWhite !important;
3128 background-color: ghostWhite !important;
3128 color: #444 !important;
3129 color: #444 !important;
3129 padding: 0 .2em !important;
3130 padding: 0 .2em !important;
3130 border: 1px solid #dedede !important;
3131 border: 1px solid #dedede !important;
3131 }
3132 }
3132
3133
3133 div.readme .readme_box pre code {
3134 div.readme .readme_box pre code {
3134 padding: 0 !important;
3135 padding: 0 !important;
3135 font-size: 12px !important;
3136 font-size: 12px !important;
3136 background-color: #eee !important;
3137 background-color: #eee !important;
3137 border: none !important;
3138 border: none !important;
3138 }
3139 }
3139
3140
3140 div.readme .readme_box pre {
3141 div.readme .readme_box pre {
3141 margin: 1em 0;
3142 margin: 1em 0;
3142 font-size: 12px;
3143 font-size: 12px;
3143 background-color: #eee;
3144 background-color: #eee;
3144 border: 1px solid #ddd;
3145 border: 1px solid #ddd;
3145 padding: 5px;
3146 padding: 5px;
3146 color: #444;
3147 color: #444;
3147 overflow: auto;
3148 overflow: auto;
3148 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3149 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3149 -webkit-border-radius: 3px;
3150 -webkit-border-radius: 3px;
3150 -moz-border-radius: 3px;
3151 -moz-border-radius: 3px;
3151 border-radius: 3px;
3152 border-radius: 3px;
3152 }
3153 }
3153
3154
3154
3155
3155 /** RST STYLE **/
3156 /** RST STYLE **/
3156
3157
3157
3158
3158 div.rst-block {
3159 div.rst-block {
3159 padding:0px;
3160 padding:0px;
3160 }
3161 }
3161
3162
3162 div.rst-block h2 {
3163 div.rst-block h2 {
3163 font-weight: normal;
3164 font-weight: normal;
3164 }
3165 }
3165
3166
3166 div.rst-block {
3167 div.rst-block {
3167 background-color: #fafafa;
3168 background-color: #fafafa;
3168 }
3169 }
3169
3170
3170 div.rst-block {
3171 div.rst-block {
3171 clear:both;
3172 clear:both;
3172 overflow:hidden;
3173 overflow:hidden;
3173 margin:0;
3174 margin:0;
3174 padding:0 20px 10px;
3175 padding:0 20px 10px;
3175 }
3176 }
3176
3177
3177 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
3178 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
3178 border-bottom: 0 !important;
3179 border-bottom: 0 !important;
3179 margin: 0 !important;
3180 margin: 0 !important;
3180 padding: 0 !important;
3181 padding: 0 !important;
3181 line-height: 1.5em !important;
3182 line-height: 1.5em !important;
3182 }
3183 }
3183
3184
3184
3185
3185 div.rst-block h1:first-child {
3186 div.rst-block h1:first-child {
3186 padding-top: .25em !important;
3187 padding-top: .25em !important;
3187 }
3188 }
3188
3189
3189 div.rst-block h2, div.rst-block h3 {
3190 div.rst-block h2, div.rst-block h3 {
3190 margin: 1em 0 !important;
3191 margin: 1em 0 !important;
3191 }
3192 }
3192
3193
3193 div.rst-block h2 {
3194 div.rst-block h2 {
3194 margin-top: 1.5em !important;
3195 margin-top: 1.5em !important;
3195 border-top: 4px solid #e0e0e0 !important;
3196 border-top: 4px solid #e0e0e0 !important;
3196 padding-top: .5em !important;
3197 padding-top: .5em !important;
3197 }
3198 }
3198
3199
3199 div.rst-block p {
3200 div.rst-block p {
3200 color: black !important;
3201 color: black !important;
3201 margin: 1em 0 !important;
3202 margin: 1em 0 !important;
3202 line-height: 1.5em !important;
3203 line-height: 1.5em !important;
3203 }
3204 }
3204
3205
3205 div.rst-block ul {
3206 div.rst-block ul {
3206 list-style: disc !important;
3207 list-style: disc !important;
3207 margin: 1em 0 1em 2em !important;
3208 margin: 1em 0 1em 2em !important;
3208 }
3209 }
3209
3210
3210 div.rst-block ol {
3211 div.rst-block ol {
3211 list-style: decimal;
3212 list-style: decimal;
3212 margin: 1em 0 1em 2em !important;
3213 margin: 1em 0 1em 2em !important;
3213 }
3214 }
3214
3215
3215 div.rst-block pre, code {
3216 div.rst-block pre, code {
3216 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3217 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3217 }
3218 }
3218
3219
3219 div.rst-block code {
3220 div.rst-block code {
3220 font-size: 12px !important;
3221 font-size: 12px !important;
3221 background-color: ghostWhite !important;
3222 background-color: ghostWhite !important;
3222 color: #444 !important;
3223 color: #444 !important;
3223 padding: 0 .2em !important;
3224 padding: 0 .2em !important;
3224 border: 1px solid #dedede !important;
3225 border: 1px solid #dedede !important;
3225 }
3226 }
3226
3227
3227 div.rst-block pre code {
3228 div.rst-block pre code {
3228 padding: 0 !important;
3229 padding: 0 !important;
3229 font-size: 12px !important;
3230 font-size: 12px !important;
3230 background-color: #eee !important;
3231 background-color: #eee !important;
3231 border: none !important;
3232 border: none !important;
3232 }
3233 }
3233
3234
3234 div.rst-block pre {
3235 div.rst-block pre {
3235 margin: 1em 0;
3236 margin: 1em 0;
3236 font-size: 12px;
3237 font-size: 12px;
3237 background-color: #eee;
3238 background-color: #eee;
3238 border: 1px solid #ddd;
3239 border: 1px solid #ddd;
3239 padding: 5px;
3240 padding: 5px;
3240 color: #444;
3241 color: #444;
3241 overflow: auto;
3242 overflow: auto;
3242 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3243 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3243 -webkit-border-radius: 3px;
3244 -webkit-border-radius: 3px;
3244 -moz-border-radius: 3px;
3245 -moz-border-radius: 3px;
3245 border-radius: 3px;
3246 border-radius: 3px;
3246 }
3247 }
3247
3248
3248
3249
3249 /** comment main **/
3250 /** comment main **/
3250 .comments {
3251 .comments {
3251 padding:10px 20px;
3252 padding:10px 20px;
3252 }
3253 }
3253
3254
3254 .comments .comment {
3255 .comments .comment {
3255 border: 1px solid #ddd;
3256 border: 1px solid #ddd;
3256 margin-top: 10px;
3257 margin-top: 10px;
3257 -webkit-border-radius: 4px;
3258 -webkit-border-radius: 4px;
3258 -moz-border-radius: 4px;
3259 -moz-border-radius: 4px;
3259 border-radius: 4px;
3260 border-radius: 4px;
3260 }
3261 }
3261
3262
3262 .comments .comment .meta {
3263 .comments .comment .meta {
3263 background: #f8f8f8;
3264 background: #f8f8f8;
3264 padding: 6px;
3265 padding: 6px;
3265 border-bottom: 1px solid #ddd;
3266 border-bottom: 1px solid #ddd;
3266 }
3267 }
3267
3268
3268 .comments .comment .meta img {
3269 .comments .comment .meta img {
3269 vertical-align: middle;
3270 vertical-align: middle;
3270 }
3271 }
3271
3272
3272 .comments .comment .meta .user {
3273 .comments .comment .meta .user {
3273 font-weight: bold;
3274 font-weight: bold;
3274 }
3275 }
3275
3276
3276 .comments .comment .meta .date {
3277 .comments .comment .meta .date {
3277 float: right;
3278 float: right;
3278 }
3279 }
3279
3280
3280 .comments .comment .text {
3281 .comments .comment .text {
3281 padding: 8px 6px 6px 14px;
3282 padding: 8px 6px 6px 14px;
3282 background-color: #FAFAFA;
3283 background-color: #FAFAFA;
3283 }
3284 }
3284
3285
3285 .comments .comments-number{
3286 .comments .comments-number{
3286 padding:0px 0px 10px 0px;
3287 padding:0px 0px 10px 0px;
3287 font-weight: bold;
3288 font-weight: bold;
3288 color: #666;
3289 color: #666;
3289 font-size: 16px;
3290 font-size: 16px;
3290 }
3291 }
3291
3292
3292 /** comment form **/
3293 /** comment form **/
3293
3294
3294 .comment-form .clearfix{
3295 .comment-form .clearfix{
3295 background: #EEE;
3296 background: #EEE;
3296 -webkit-border-radius: 4px;
3297 -webkit-border-radius: 4px;
3297 -moz-border-radius: 4px;
3298 -moz-border-radius: 4px;
3298 border-radius: 4px;
3299 border-radius: 4px;
3299 padding: 10px;
3300 padding: 10px;
3300 }
3301 }
3301
3302
3302 div.comment-form {
3303 div.comment-form {
3303 margin-top: 20px;
3304 margin-top: 20px;
3304 }
3305 }
3305
3306
3306 .comment-form strong {
3307 .comment-form strong {
3307 display: block;
3308 display: block;
3308 margin-bottom: 15px;
3309 margin-bottom: 15px;
3309 }
3310 }
3310
3311
3311 .comment-form textarea {
3312 .comment-form textarea {
3312 width: 100%;
3313 width: 100%;
3313 height: 100px;
3314 height: 100px;
3314 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
3315 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
3315 }
3316 }
3316
3317
3317 form.comment-form {
3318 form.comment-form {
3318 margin-top: 10px;
3319 margin-top: 10px;
3319 margin-left: 10px;
3320 margin-left: 10px;
3320 }
3321 }
3321
3322
3322 .comment-form-submit {
3323 .comment-form-submit {
3323 margin-top: 5px;
3324 margin-top: 5px;
3324 margin-left: 525px;
3325 margin-left: 525px;
3325 }
3326 }
3326
3327
3327 .file-comments {
3328 .file-comments {
3328 display: none;
3329 display: none;
3329 }
3330 }
3330
3331
3331 .comment-form .comment {
3332 .comment-form .comment {
3332 margin-left: 10px;
3333 margin-left: 10px;
3333 }
3334 }
3334
3335
3335 .comment-form .comment-help{
3336 .comment-form .comment-help{
3336 padding: 0px 0px 5px 0px;
3337 padding: 0px 0px 5px 0px;
3337 color: #666;
3338 color: #666;
3338 }
3339 }
3339
3340
3340 .comment-form .comment-button{
3341 .comment-form .comment-button{
3341 padding-top:5px;
3342 padding-top:5px;
3342 }
3343 }
3343
3344
3344 .add-another-button {
3345 .add-another-button {
3345 margin-left: 10px;
3346 margin-left: 10px;
3346 margin-top: 10px;
3347 margin-top: 10px;
3347 margin-bottom: 10px;
3348 margin-bottom: 10px;
3348 }
3349 }
3349
3350
3350 .comment .buttons {
3351 .comment .buttons {
3351 position: absolute;
3352 position: absolute;
3352 right:40px;
3353 right:40px;
3353 }
3354 }
3354
3355
3355
3356
3356 .show-inline-comments{
3357 .show-inline-comments{
3357 position: relative;
3358 position: relative;
3358 top:1px
3359 top:1px
3359 }
3360 }
3360
3361
3361 /** comment inline form **/
3362 /** comment inline form **/
3362
3363
3363 .comment-inline-form .clearfix{
3364 .comment-inline-form .clearfix{
3364 background: #EEE;
3365 background: #EEE;
3365 -webkit-border-radius: 4px;
3366 -webkit-border-radius: 4px;
3366 -moz-border-radius: 4px;
3367 -moz-border-radius: 4px;
3367 border-radius: 4px;
3368 border-radius: 4px;
3368 padding: 5px;
3369 padding: 5px;
3369 }
3370 }
3370
3371
3371 div.comment-inline-form {
3372 div.comment-inline-form {
3372 margin-top: 5px;
3373 margin-top: 5px;
3373 padding:2px 6px 8px 6px;
3374 padding:2px 6px 8px 6px;
3374 }
3375 }
3375
3376
3376 .comment-inline-form strong {
3377 .comment-inline-form strong {
3377 display: block;
3378 display: block;
3378 margin-bottom: 15px;
3379 margin-bottom: 15px;
3379 }
3380 }
3380
3381
3381 .comment-inline-form textarea {
3382 .comment-inline-form textarea {
3382 width: 100%;
3383 width: 100%;
3383 height: 100px;
3384 height: 100px;
3384 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
3385 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
3385 }
3386 }
3386
3387
3387 form.comment-inline-form {
3388 form.comment-inline-form {
3388 margin-top: 10px;
3389 margin-top: 10px;
3389 margin-left: 10px;
3390 margin-left: 10px;
3390 }
3391 }
3391
3392
3392 .comment-inline-form-submit {
3393 .comment-inline-form-submit {
3393 margin-top: 5px;
3394 margin-top: 5px;
3394 margin-left: 525px;
3395 margin-left: 525px;
3395 }
3396 }
3396
3397
3397 .file-comments {
3398 .file-comments {
3398 display: none;
3399 display: none;
3399 }
3400 }
3400
3401
3401 .comment-inline-form .comment {
3402 .comment-inline-form .comment {
3402 margin-left: 10px;
3403 margin-left: 10px;
3403 }
3404 }
3404
3405
3405 .comment-inline-form .comment-help{
3406 .comment-inline-form .comment-help{
3406 padding: 0px 0px 2px 0px;
3407 padding: 0px 0px 2px 0px;
3407 color: #666666;
3408 color: #666666;
3408 font-size: 10px;
3409 font-size: 10px;
3409 }
3410 }
3410
3411
3411 .comment-inline-form .comment-button{
3412 .comment-inline-form .comment-button{
3412 padding-top:5px;
3413 padding-top:5px;
3413 }
3414 }
3414
3415
3415 /** comment inline **/
3416 /** comment inline **/
3416 .inline-comments {
3417 .inline-comments {
3417 padding:10px 20px;
3418 padding:10px 20px;
3418 }
3419 }
3419
3420
3420 .inline-comments div.rst-block {
3421 .inline-comments div.rst-block {
3421 clear:both;
3422 clear:both;
3422 overflow:hidden;
3423 overflow:hidden;
3423 margin:0;
3424 margin:0;
3424 padding:0 20px 0px;
3425 padding:0 20px 0px;
3425 }
3426 }
3426 .inline-comments .comment {
3427 .inline-comments .comment {
3427 border: 1px solid #ddd;
3428 border: 1px solid #ddd;
3428 -webkit-border-radius: 4px;
3429 -webkit-border-radius: 4px;
3429 -moz-border-radius: 4px;
3430 -moz-border-radius: 4px;
3430 border-radius: 4px;
3431 border-radius: 4px;
3431 margin: 3px 3px 5px 5px;
3432 margin: 3px 3px 5px 5px;
3432 }
3433 }
3433
3434
3434 .inline-comments .comment .meta {
3435 .inline-comments .comment .meta {
3435 background: #f8f8f8;
3436 background: #f8f8f8;
3436 padding: 6px;
3437 padding: 6px;
3437 border-bottom: 1px solid #ddd;
3438 border-bottom: 1px solid #ddd;
3438 }
3439 }
3439
3440
3440 .inline-comments .comment .meta img {
3441 .inline-comments .comment .meta img {
3441 vertical-align: middle;
3442 vertical-align: middle;
3442 }
3443 }
3443
3444
3444 .inline-comments .comment .meta .user {
3445 .inline-comments .comment .meta .user {
3445 font-weight: bold;
3446 font-weight: bold;
3446 }
3447 }
3447
3448
3448 .inline-comments .comment .meta .date {
3449 .inline-comments .comment .meta .date {
3449 float: right;
3450 float: right;
3450 }
3451 }
3451
3452
3452 .inline-comments .comment .text {
3453 .inline-comments .comment .text {
3453 padding: 8px 6px 6px 14px;
3454 padding: 8px 6px 6px 14px;
3454 background-color: #FAFAFA;
3455 background-color: #FAFAFA;
3455 }
3456 }
3456
3457
3457 .inline-comments .comments-number{
3458 .inline-comments .comments-number{
3458 padding:0px 0px 10px 0px;
3459 padding:0px 0px 10px 0px;
3459 font-weight: bold;
3460 font-weight: bold;
3460 color: #666;
3461 color: #666;
3461 font-size: 16px;
3462 font-size: 16px;
3462 }
3463 }
3463 .inline-comments-button .add-comment{
3464 .inline-comments-button .add-comment{
3464 margin:10px 5px !important;
3465 margin:10px 5px !important;
3465 }
3466 }
3466 .notifications{
3467 .notifications{
3467 width:22px;
3468 width:22px;
3468 padding:2px;
3469 padding:2px;
3469 float:right;
3470 float:right;
3470 -webkit-border-radius: 4px;
3471 -webkit-border-radius: 4px;
3471 -moz-border-radius: 4px;
3472 -moz-border-radius: 4px;
3472 border-radius: 4px;
3473 border-radius: 4px;
3473 text-align: center;
3474 text-align: center;
3474 margin: 0px -10px 0px 5px;
3475 margin: 0px -10px 0px 5px;
3475 background-color: #DEDEDE;
3476 background-color: #DEDEDE;
3476 }
3477 }
3477 .notifications a{
3478 .notifications a{
3478 color:#888 !important;
3479 color:#888 !important;
3479 display: block;
3480 display: block;
3480 font-size: 10px
3481 font-size: 10px
3481 }
3482 }
3482 .notifications a:hover{
3483 .notifications a:hover{
3483 text-decoration: none !important;
3484 text-decoration: none !important;
3485 }
3486 .notification-header{
3487
3488 }
3489 .notification-header .desc{
3490 font-size: 16px;
3491 height: 24px;
3492 padding-top: 6px;
3493 float: left
3494 }
3495
3496 .notification-header .desc.unread{
3497 font-weight: bold;
3498 font-size: 17px;
3499 }
3500
3501 .notification-header .delete-notifications{
3502 float: right;
3503 padding-top: 8px;
3504 cursor: pointer;
3505 }
3506 .notification-subject{
3507 clear:both;
3508 border-bottom: 1px solid #eee;
3509 padding:5px 0px 5px 38px;
3484 } No newline at end of file
3510 }
@@ -1,565 +1,581 b''
1 /**
1 /**
2 RhodeCode JS Files
2 RhodeCode JS Files
3 **/
3 **/
4
4
5 if (typeof console == "undefined" || typeof console.log == "undefined"){
5 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 console = { log: function() {} }
6 console = { log: function() {} }
7 }
7 }
8
8
9
9
10 var str_repeat = function(i, m) {
10 var str_repeat = function(i, m) {
11 for (var o = []; m > 0; o[--m] = i);
11 for (var o = []; m > 0; o[--m] = i);
12 return o.join('');
12 return o.join('');
13 };
13 };
14
14
15 /**
15 /**
16 * INJECT .format function into String
16 * INJECT .format function into String
17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
18 * Return "My name is Johny Bravo"
18 * Return "My name is Johny Bravo"
19 * Inspired by https://gist.github.com/1049426
19 * Inspired by https://gist.github.com/1049426
20 */
20 */
21 String.prototype.format = function() {
21 String.prototype.format = function() {
22
22
23 function format() {
23 function format() {
24 var str = this;
24 var str = this;
25 var len = arguments.length+1;
25 var len = arguments.length+1;
26 var safe = undefined;
26 var safe = undefined;
27 var arg = undefined;
27 var arg = undefined;
28
28
29 // For each {0} {1} {n...} replace with the argument in that position. If
29 // For each {0} {1} {n...} replace with the argument in that position. If
30 // the argument is an object or an array it will be stringified to JSON.
30 // the argument is an object or an array it will be stringified to JSON.
31 for (var i=0; i < len; arg = arguments[i++]) {
31 for (var i=0; i < len; arg = arguments[i++]) {
32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
34 }
34 }
35 return str;
35 return str;
36 }
36 }
37
37
38 // Save a reference of what may already exist under the property native.
38 // Save a reference of what may already exist under the property native.
39 // Allows for doing something like: if("".format.native) { /* use native */ }
39 // Allows for doing something like: if("".format.native) { /* use native */ }
40 format.native = String.prototype.format;
40 format.native = String.prototype.format;
41
41
42 // Replace the prototype property
42 // Replace the prototype property
43 return format;
43 return format;
44
44
45 }();
45 }();
46
46
47
47
48 /**
48 /**
49 * SmartColorGenerator
49 * SmartColorGenerator
50 *
50 *
51 *usage::
51 *usage::
52 * var CG = new ColorGenerator();
52 * var CG = new ColorGenerator();
53 * var col = CG.getColor(key); //returns array of RGB
53 * var col = CG.getColor(key); //returns array of RGB
54 * 'rgb({0})'.format(col.join(',')
54 * 'rgb({0})'.format(col.join(',')
55 *
55 *
56 * @returns {ColorGenerator}
56 * @returns {ColorGenerator}
57 */
57 */
58 var ColorGenerator = function(){
58 var ColorGenerator = function(){
59 this.GOLDEN_RATIO = 0.618033988749895;
59 this.GOLDEN_RATIO = 0.618033988749895;
60 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
60 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
61 this.HSV_1 = 0.75;//saturation
61 this.HSV_1 = 0.75;//saturation
62 this.HSV_2 = 0.95;
62 this.HSV_2 = 0.95;
63 this.color;
63 this.color;
64 this.cacheColorMap = {};
64 this.cacheColorMap = {};
65 };
65 };
66
66
67 ColorGenerator.prototype = {
67 ColorGenerator.prototype = {
68 getColor:function(key){
68 getColor:function(key){
69 if(this.cacheColorMap[key] !== undefined){
69 if(this.cacheColorMap[key] !== undefined){
70 return this.cacheColorMap[key];
70 return this.cacheColorMap[key];
71 }
71 }
72 else{
72 else{
73 this.cacheColorMap[key] = this.generateColor();
73 this.cacheColorMap[key] = this.generateColor();
74 return this.cacheColorMap[key];
74 return this.cacheColorMap[key];
75 }
75 }
76 },
76 },
77 _hsvToRgb:function(h,s,v){
77 _hsvToRgb:function(h,s,v){
78 if (s == 0.0)
78 if (s == 0.0)
79 return [v, v, v];
79 return [v, v, v];
80 i = parseInt(h * 6.0)
80 i = parseInt(h * 6.0)
81 f = (h * 6.0) - i
81 f = (h * 6.0) - i
82 p = v * (1.0 - s)
82 p = v * (1.0 - s)
83 q = v * (1.0 - s * f)
83 q = v * (1.0 - s * f)
84 t = v * (1.0 - s * (1.0 - f))
84 t = v * (1.0 - s * (1.0 - f))
85 i = i % 6
85 i = i % 6
86 if (i == 0)
86 if (i == 0)
87 return [v, t, p]
87 return [v, t, p]
88 if (i == 1)
88 if (i == 1)
89 return [q, v, p]
89 return [q, v, p]
90 if (i == 2)
90 if (i == 2)
91 return [p, v, t]
91 return [p, v, t]
92 if (i == 3)
92 if (i == 3)
93 return [p, q, v]
93 return [p, q, v]
94 if (i == 4)
94 if (i == 4)
95 return [t, p, v]
95 return [t, p, v]
96 if (i == 5)
96 if (i == 5)
97 return [v, p, q]
97 return [v, p, q]
98 },
98 },
99 generateColor:function(){
99 generateColor:function(){
100 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
100 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
101 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
101 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
102 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
102 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
103 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
103 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
104 function toRgb(v){
104 function toRgb(v){
105 return ""+parseInt(v*256)
105 return ""+parseInt(v*256)
106 }
106 }
107 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
107 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
108
108
109 }
109 }
110 }
110 }
111
111
112
112
113
113
114
114
115
115
116 /**
116 /**
117 * GLOBAL YUI Shortcuts
117 * GLOBAL YUI Shortcuts
118 */
118 */
119 var YUC = YAHOO.util.Connect;
119 var YUC = YAHOO.util.Connect;
120 var YUD = YAHOO.util.Dom;
120 var YUD = YAHOO.util.Dom;
121 var YUE = YAHOO.util.Event;
121 var YUE = YAHOO.util.Event;
122 var YUQ = YAHOO.util.Selector.query;
122 var YUQ = YAHOO.util.Selector.query;
123
123
124 // defines if push state is enabled for this browser ?
124 // defines if push state is enabled for this browser ?
125 var push_state_enabled = Boolean(
125 var push_state_enabled = Boolean(
126 window.history && window.history.pushState && window.history.replaceState
126 window.history && window.history.pushState && window.history.replaceState
127 && !( /* disable for versions of iOS before version 4.3 (8F190) */
127 && !( /* disable for versions of iOS before version 4.3 (8F190) */
128 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
128 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
129 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
129 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
130 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
130 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
131 )
131 )
132 );
132 );
133
133
134 /**
134 /**
135 * Partial Ajax Implementation
135 * Partial Ajax Implementation
136 *
136 *
137 * @param url: defines url to make partial request
137 * @param url: defines url to make partial request
138 * @param container: defines id of container to input partial result
138 * @param container: defines id of container to input partial result
139 * @param s_call: success callback function that takes o as arg
139 * @param s_call: success callback function that takes o as arg
140 * o.tId
140 * o.tId
141 * o.status
141 * o.status
142 * o.statusText
142 * o.statusText
143 * o.getResponseHeader[ ]
143 * o.getResponseHeader[ ]
144 * o.getAllResponseHeaders
144 * o.getAllResponseHeaders
145 * o.responseText
145 * o.responseText
146 * o.responseXML
146 * o.responseXML
147 * o.argument
147 * o.argument
148 * @param f_call: failure callback
148 * @param f_call: failure callback
149 * @param args arguments
149 * @param args arguments
150 */
150 */
151 function ypjax(url,container,s_call,f_call,args){
151 function ypjax(url,container,s_call,f_call,args){
152 var method='GET';
152 var method='GET';
153 if(args===undefined){
153 if(args===undefined){
154 args=null;
154 args=null;
155 }
155 }
156
156
157 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
157 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
158 YUC.initHeader('X-PARTIAL-XHR',true);
158 YUC.initHeader('X-PARTIAL-XHR',true);
159
159
160 // wrapper of passed callback
160 // wrapper of passed callback
161 var s_wrapper = (function(o){
161 var s_wrapper = (function(o){
162 return function(o){
162 return function(o){
163 YUD.get(container).innerHTML=o.responseText;
163 YUD.get(container).innerHTML=o.responseText;
164 YUD.setStyle(container,'opacity','1.0');
164 YUD.setStyle(container,'opacity','1.0');
165 //execute the given original callback
165 //execute the given original callback
166 if (s_call !== undefined){
166 if (s_call !== undefined){
167 s_call(o);
167 s_call(o);
168 }
168 }
169 }
169 }
170 })()
170 })()
171 YUD.setStyle(container,'opacity','0.3');
171 YUD.setStyle(container,'opacity','0.3');
172 YUC.asyncRequest(method,url,{
172 YUC.asyncRequest(method,url,{
173 success:s_wrapper,
173 success:s_wrapper,
174 failure:function(o){
174 failure:function(o){
175 console.log(o);
175 console.log(o);
176 YUD.get(container).innerHTML='ERROR';
176 YUD.get(container).innerHTML='ERROR';
177 YUD.setStyle(container,'opacity','1.0');
177 YUD.setStyle(container,'opacity','1.0');
178 YUD.setStyle(container,'color','red');
178 YUD.setStyle(container,'color','red');
179 }
179 }
180 },args);
180 },args);
181
181
182 };
182 };
183
183
184 /**
184 /**
185 * tooltip activate
185 * tooltip activate
186 */
186 */
187 var tooltip_activate = function(){
187 var tooltip_activate = function(){
188 function toolTipsId(){
188 function toolTipsId(){
189 var ids = [];
189 var ids = [];
190 var tts = YUQ('.tooltip');
190 var tts = YUQ('.tooltip');
191 for (var i = 0; i < tts.length; i++) {
191 for (var i = 0; i < tts.length; i++) {
192 // if element doesn't not have and id
192 // if element doesn't not have and id
193 // autogenerate one for tooltip
193 // autogenerate one for tooltip
194 if (!tts[i].id){
194 if (!tts[i].id){
195 tts[i].id='tt'+((i*100)+tts.length);
195 tts[i].id='tt'+((i*100)+tts.length);
196 }
196 }
197 ids.push(tts[i].id);
197 ids.push(tts[i].id);
198 }
198 }
199 return ids
199 return ids
200 };
200 };
201 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
201 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
202 context: [[toolTipsId()],"tl","bl",null,[0,5]],
202 context: [[toolTipsId()],"tl","bl",null,[0,5]],
203 monitorresize:false,
203 monitorresize:false,
204 xyoffset :[0,0],
204 xyoffset :[0,0],
205 autodismissdelay:300000,
205 autodismissdelay:300000,
206 hidedelay:5,
206 hidedelay:5,
207 showdelay:20,
207 showdelay:20,
208 });
208 });
209 };
209 };
210
210
211 /**
211 /**
212 * show more
212 * show more
213 */
213 */
214 var show_more_event = function(){
214 var show_more_event = function(){
215 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
215 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
216 var el = e.target;
216 var el = e.target;
217 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
217 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
218 YUD.setStyle(el.parentNode,'display','none');
218 YUD.setStyle(el.parentNode,'display','none');
219 });
219 });
220 };
220 };
221
221
222
222
223 /**
223 /**
224 * Quick filter widget
224 * Quick filter widget
225 *
225 *
226 * @param target: filter input target
226 * @param target: filter input target
227 * @param nodes: list of nodes in html we want to filter.
227 * @param nodes: list of nodes in html we want to filter.
228 * @param display_element function that takes current node from nodes and
228 * @param display_element function that takes current node from nodes and
229 * does hide or show based on the node
229 * does hide or show based on the node
230 *
230 *
231 */
231 */
232 var q_filter = function(target,nodes,display_element){
232 var q_filter = function(target,nodes,display_element){
233
233
234 var nodes = nodes;
234 var nodes = nodes;
235 var q_filter_field = YUD.get(target);
235 var q_filter_field = YUD.get(target);
236 var F = YAHOO.namespace(target);
236 var F = YAHOO.namespace(target);
237
237
238 YUE.on(q_filter_field,'click',function(){
238 YUE.on(q_filter_field,'click',function(){
239 q_filter_field.value = '';
239 q_filter_field.value = '';
240 });
240 });
241
241
242 YUE.on(q_filter_field,'keyup',function(e){
242 YUE.on(q_filter_field,'keyup',function(e){
243 clearTimeout(F.filterTimeout);
243 clearTimeout(F.filterTimeout);
244 F.filterTimeout = setTimeout(F.updateFilter,600);
244 F.filterTimeout = setTimeout(F.updateFilter,600);
245 });
245 });
246
246
247 F.filterTimeout = null;
247 F.filterTimeout = null;
248
248
249 var show_node = function(node){
249 var show_node = function(node){
250 YUD.setStyle(node,'display','')
250 YUD.setStyle(node,'display','')
251 }
251 }
252 var hide_node = function(node){
252 var hide_node = function(node){
253 YUD.setStyle(node,'display','none');
253 YUD.setStyle(node,'display','none');
254 }
254 }
255
255
256 F.updateFilter = function() {
256 F.updateFilter = function() {
257 // Reset timeout
257 // Reset timeout
258 F.filterTimeout = null;
258 F.filterTimeout = null;
259
259
260 var obsolete = [];
260 var obsolete = [];
261
261
262 var req = q_filter_field.value.toLowerCase();
262 var req = q_filter_field.value.toLowerCase();
263
263
264 var l = nodes.length;
264 var l = nodes.length;
265 var i;
265 var i;
266 var showing = 0;
266 var showing = 0;
267
267
268 for (i=0;i<l;i++ ){
268 for (i=0;i<l;i++ ){
269 var n = nodes[i];
269 var n = nodes[i];
270 var target_element = display_element(n)
270 var target_element = display_element(n)
271 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
271 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
272 hide_node(target_element);
272 hide_node(target_element);
273 }
273 }
274 else{
274 else{
275 show_node(target_element);
275 show_node(target_element);
276 showing+=1;
276 showing+=1;
277 }
277 }
278 }
278 }
279
279
280 // if repo_count is set update the number
280 // if repo_count is set update the number
281 var cnt = YUD.get('repo_count');
281 var cnt = YUD.get('repo_count');
282 if(cnt){
282 if(cnt){
283 YUD.get('repo_count').innerHTML = showing;
283 YUD.get('repo_count').innerHTML = showing;
284 }
284 }
285
285
286 }
286 }
287 };
287 };
288
288
289 var ajaxPOST = function(url,postData,success) {
289 var ajaxPOST = function(url,postData,success) {
290 var sUrl = url;
290 var sUrl = url;
291 var callback = {
291 var callback = {
292 success: success,
292 success: success,
293 failure: function (o) {
293 failure: function (o) {
294 alert("error");
294 alert("error");
295 },
295 },
296 };
296 };
297 var postData = postData;
297 var postData = postData;
298 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
298 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
299 };
299 };
300
300
301
301
302 /** comments **/
302 /** comments **/
303 var removeInlineForm = function(form) {
303 var removeInlineForm = function(form) {
304 form.parentNode.removeChild(form);
304 form.parentNode.removeChild(form);
305 };
305 };
306
306
307 var tableTr = function(cls,body){
307 var tableTr = function(cls,body){
308 var form = document.createElement('tr');
308 var form = document.createElement('tr');
309 YUD.addClass(form, cls);
309 YUD.addClass(form, cls);
310 form.innerHTML = '<td class="lineno-inline new-inline"></td>'+
310 form.innerHTML = '<td class="lineno-inline new-inline"></td>'+
311 '<td class="lineno-inline old-inline"></td>'+
311 '<td class="lineno-inline old-inline"></td>'+
312 '<td>{0}</td>'.format(body);
312 '<td>{0}</td>'.format(body);
313 return form;
313 return form;
314 };
314 };
315
315
316 var createInlineForm = function(parent_tr, f_path, line) {
316 var createInlineForm = function(parent_tr, f_path, line) {
317 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
317 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
318 tmpl = tmpl.format(f_path, line);
318 tmpl = tmpl.format(f_path, line);
319 var form = tableTr('comment-form-inline',tmpl)
319 var form = tableTr('comment-form-inline',tmpl)
320
320
321 // create event for hide button
321 // create event for hide button
322 form = new YAHOO.util.Element(form);
322 form = new YAHOO.util.Element(form);
323 var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
323 var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
324 form_hide_button.on('click', function(e) {
324 form_hide_button.on('click', function(e) {
325 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
325 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
326 removeInlineForm(newtr);
326 removeInlineForm(newtr);
327 YUD.removeClass(parent_tr, 'form-open');
327 YUD.removeClass(parent_tr, 'form-open');
328 });
328 });
329 return form
329 return form
330 };
330 };
331 var injectInlineForm = function(tr){
331 var injectInlineForm = function(tr){
332 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){
332 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){
333 return
333 return
334 }
334 }
335 YUD.addClass(tr,'form-open');
335 YUD.addClass(tr,'form-open');
336 var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
336 var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
337 var f_path = YUD.getAttribute(node,'path');
337 var f_path = YUD.getAttribute(node,'path');
338 var lineno = getLineNo(tr);
338 var lineno = getLineNo(tr);
339 var form = createInlineForm(tr, f_path, lineno);
339 var form = createInlineForm(tr, f_path, lineno);
340 var target_tr = tr;
340 var target_tr = tr;
341 if(YUD.hasClass(YUD.getNextSibling(tr),'inline-comments')){
341 if(YUD.hasClass(YUD.getNextSibling(tr),'inline-comments')){
342 target_tr = YUD.getNextSibling(tr);
342 target_tr = YUD.getNextSibling(tr);
343 }
343 }
344 YUD.insertAfter(form,target_tr);
344 YUD.insertAfter(form,target_tr);
345 YUD.get('text_'+lineno).focus();
345 YUD.get('text_'+lineno).focus();
346 };
346 };
347
347
348 var createInlineAddButton = function(tr,label){
348 var createInlineAddButton = function(tr,label){
349 var html = '<div class="add-comment"><span class="ui-button-small">{0}</span></div>'.format(label);
349 var html = '<div class="add-comment"><span class="ui-button-small">{0}</span></div>'.format(label);
350
350
351 var add = new YAHOO.util.Element(tableTr('inline-comments-button',html));
351 var add = new YAHOO.util.Element(tableTr('inline-comments-button',html));
352 add.on('click', function(e) {
352 add.on('click', function(e) {
353 injectInlineForm(tr);
353 injectInlineForm(tr);
354 });
354 });
355 return add;
355 return add;
356 };
356 };
357
357
358 var getLineNo = function(tr) {
358 var getLineNo = function(tr) {
359 var line;
359 var line;
360 var o = tr.children[0].id.split('_');
360 var o = tr.children[0].id.split('_');
361 var n = tr.children[1].id.split('_');
361 var n = tr.children[1].id.split('_');
362
362
363 if (n.length >= 2) {
363 if (n.length >= 2) {
364 line = n[n.length-1];
364 line = n[n.length-1];
365 } else if (o.length >= 2) {
365 } else if (o.length >= 2) {
366 line = o[o.length-1];
366 line = o[o.length-1];
367 }
367 }
368
368
369 return line
369 return line
370 };
370 };
371
371
372
372
373 var fileBrowserListeners = function(current_url, node_list_url, url_base,
373 var fileBrowserListeners = function(current_url, node_list_url, url_base,
374 truncated_lbl, nomatch_lbl){
374 truncated_lbl, nomatch_lbl){
375 var current_url_branch = +"?branch=__BRANCH__";
375 var current_url_branch = +"?branch=__BRANCH__";
376 var url = url_base;
376 var url = url_base;
377 var node_url = node_list_url;
377 var node_url = node_list_url;
378
378
379 YUE.on('stay_at_branch','click',function(e){
379 YUE.on('stay_at_branch','click',function(e){
380 if(e.target.checked){
380 if(e.target.checked){
381 var uri = current_url_branch;
381 var uri = current_url_branch;
382 uri = uri.replace('__BRANCH__',e.target.value);
382 uri = uri.replace('__BRANCH__',e.target.value);
383 window.location = uri;
383 window.location = uri;
384 }
384 }
385 else{
385 else{
386 window.location = current_url;
386 window.location = current_url;
387 }
387 }
388 })
388 })
389
389
390 var n_filter = YUD.get('node_filter');
390 var n_filter = YUD.get('node_filter');
391 var F = YAHOO.namespace('node_filter');
391 var F = YAHOO.namespace('node_filter');
392
392
393 F.filterTimeout = null;
393 F.filterTimeout = null;
394 var nodes = null;
394 var nodes = null;
395
395
396 F.initFilter = function(){
396 F.initFilter = function(){
397 YUD.setStyle('node_filter_box_loading','display','');
397 YUD.setStyle('node_filter_box_loading','display','');
398 YUD.setStyle('search_activate_id','display','none');
398 YUD.setStyle('search_activate_id','display','none');
399 YUD.setStyle('add_node_id','display','none');
399 YUD.setStyle('add_node_id','display','none');
400 YUC.initHeader('X-PARTIAL-XHR',true);
400 YUC.initHeader('X-PARTIAL-XHR',true);
401 YUC.asyncRequest('GET',url,{
401 YUC.asyncRequest('GET',url,{
402 success:function(o){
402 success:function(o){
403 nodes = JSON.parse(o.responseText);
403 nodes = JSON.parse(o.responseText);
404 YUD.setStyle('node_filter_box_loading','display','none');
404 YUD.setStyle('node_filter_box_loading','display','none');
405 YUD.setStyle('node_filter_box','display','');
405 YUD.setStyle('node_filter_box','display','');
406 },
406 },
407 failure:function(o){
407 failure:function(o){
408 console.log('failed to load');
408 console.log('failed to load');
409 }
409 }
410 },null);
410 },null);
411 }
411 }
412
412
413 F.updateFilter = function(e) {
413 F.updateFilter = function(e) {
414
414
415 return function(){
415 return function(){
416 // Reset timeout
416 // Reset timeout
417 F.filterTimeout = null;
417 F.filterTimeout = null;
418 var query = e.target.value;
418 var query = e.target.value;
419 var match = [];
419 var match = [];
420 var matches = 0;
420 var matches = 0;
421 var matches_max = 20;
421 var matches_max = 20;
422 if (query != ""){
422 if (query != ""){
423 for(var i=0;i<nodes.length;i++){
423 for(var i=0;i<nodes.length;i++){
424 var pos = nodes[i].toLowerCase().indexOf(query)
424 var pos = nodes[i].toLowerCase().indexOf(query)
425 if(query && pos != -1){
425 if(query && pos != -1){
426
426
427 matches++
427 matches++
428 //show only certain amount to not kill browser
428 //show only certain amount to not kill browser
429 if (matches > matches_max){
429 if (matches > matches_max){
430 break;
430 break;
431 }
431 }
432
432
433 var n = nodes[i];
433 var n = nodes[i];
434 var n_hl = n.substring(0,pos)
434 var n_hl = n.substring(0,pos)
435 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
435 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
436 +n.substring(pos+query.length)
436 +n.substring(pos+query.length)
437 match.push('<tr><td><a class="browser-file" href="{0}">{1}</a></td><td colspan="5"></td></tr>'.format(node_url.replace('__FPATH__',n),n_hl));
437 match.push('<tr><td><a class="browser-file" href="{0}">{1}</a></td><td colspan="5"></td></tr>'.format(node_url.replace('__FPATH__',n),n_hl));
438 }
438 }
439 if(match.length >= matches_max){
439 if(match.length >= matches_max){
440 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(truncated_lbl));
440 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(truncated_lbl));
441 }
441 }
442
442
443 }
443 }
444 }
444 }
445 if(query != ""){
445 if(query != ""){
446 YUD.setStyle('tbody','display','none');
446 YUD.setStyle('tbody','display','none');
447 YUD.setStyle('tbody_filtered','display','');
447 YUD.setStyle('tbody_filtered','display','');
448
448
449 if (match.length==0){
449 if (match.length==0){
450 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(nomatch_lbl));
450 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(nomatch_lbl));
451 }
451 }
452
452
453 YUD.get('tbody_filtered').innerHTML = match.join("");
453 YUD.get('tbody_filtered').innerHTML = match.join("");
454 }
454 }
455 else{
455 else{
456 YUD.setStyle('tbody','display','');
456 YUD.setStyle('tbody','display','');
457 YUD.setStyle('tbody_filtered','display','none');
457 YUD.setStyle('tbody_filtered','display','none');
458 }
458 }
459
459
460 }
460 }
461 };
461 };
462
462
463 YUE.on(YUD.get('filter_activate'),'click',function(){
463 YUE.on(YUD.get('filter_activate'),'click',function(){
464 F.initFilter();
464 F.initFilter();
465 })
465 })
466 YUE.on(n_filter,'click',function(){
466 YUE.on(n_filter,'click',function(){
467 n_filter.value = '';
467 n_filter.value = '';
468 });
468 });
469 YUE.on(n_filter,'keyup',function(e){
469 YUE.on(n_filter,'keyup',function(e){
470 clearTimeout(F.filterTimeout);
470 clearTimeout(F.filterTimeout);
471 F.filterTimeout = setTimeout(F.updateFilter(e),600);
471 F.filterTimeout = setTimeout(F.updateFilter(e),600);
472 });
472 });
473 };
473 };
474
474
475
475
476 var initCodeMirror = function(textAreadId,resetUrl){
476 var initCodeMirror = function(textAreadId,resetUrl){
477 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
477 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
478 mode: "null",
478 mode: "null",
479 lineNumbers:true
479 lineNumbers:true
480 });
480 });
481 YUE.on('reset','click',function(e){
481 YUE.on('reset','click',function(e){
482 window.location=resetUrl
482 window.location=resetUrl
483 });
483 });
484
484
485 YUE.on('file_enable','click',function(){
485 YUE.on('file_enable','click',function(){
486 YUD.setStyle('editor_container','display','');
486 YUD.setStyle('editor_container','display','');
487 YUD.setStyle('upload_file_container','display','none');
487 YUD.setStyle('upload_file_container','display','none');
488 YUD.setStyle('filename_container','display','');
488 YUD.setStyle('filename_container','display','');
489 });
489 });
490
490
491 YUE.on('upload_file_enable','click',function(){
491 YUE.on('upload_file_enable','click',function(){
492 YUD.setStyle('editor_container','display','none');
492 YUD.setStyle('editor_container','display','none');
493 YUD.setStyle('upload_file_container','display','');
493 YUD.setStyle('upload_file_container','display','');
494 YUD.setStyle('filename_container','display','none');
494 YUD.setStyle('filename_container','display','none');
495 });
495 });
496 };
496 };
497
497
498
498
499
499
500 var getIdentNode = function(n){
500 var getIdentNode = function(n){
501 //iterate thru nodes untill matched interesting node !
501 //iterate thru nodes untill matched interesting node !
502
502
503 if (typeof n == 'undefined'){
503 if (typeof n == 'undefined'){
504 return -1
504 return -1
505 }
505 }
506
506
507 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
507 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
508 return n
508 return n
509 }
509 }
510 else{
510 else{
511 return getIdentNode(n.parentNode);
511 return getIdentNode(n.parentNode);
512 }
512 }
513 };
513 };
514
514
515 var getSelectionLink = function(selection_link_label) {
515 var getSelectionLink = function(selection_link_label) {
516 return function(){
516 return function(){
517 //get selection from start/to nodes
517 //get selection from start/to nodes
518 if (typeof window.getSelection != "undefined") {
518 if (typeof window.getSelection != "undefined") {
519 s = window.getSelection();
519 s = window.getSelection();
520
520
521 from = getIdentNode(s.anchorNode);
521 from = getIdentNode(s.anchorNode);
522 till = getIdentNode(s.focusNode);
522 till = getIdentNode(s.focusNode);
523
523
524 f_int = parseInt(from.id.replace('L',''));
524 f_int = parseInt(from.id.replace('L',''));
525 t_int = parseInt(till.id.replace('L',''));
525 t_int = parseInt(till.id.replace('L',''));
526
526
527 if (f_int > t_int){
527 if (f_int > t_int){
528 //highlight from bottom
528 //highlight from bottom
529 offset = -35;
529 offset = -35;
530 ranges = [t_int,f_int];
530 ranges = [t_int,f_int];
531
531
532 }
532 }
533 else{
533 else{
534 //highligth from top
534 //highligth from top
535 offset = 35;
535 offset = 35;
536 ranges = [f_int,t_int];
536 ranges = [f_int,t_int];
537 }
537 }
538
538
539 if (ranges[0] != ranges[1]){
539 if (ranges[0] != ranges[1]){
540 if(YUD.get('linktt') == null){
540 if(YUD.get('linktt') == null){
541 hl_div = document.createElement('div');
541 hl_div = document.createElement('div');
542 hl_div.id = 'linktt';
542 hl_div.id = 'linktt';
543 }
543 }
544 anchor = '#L'+ranges[0]+'-'+ranges[1];
544 anchor = '#L'+ranges[0]+'-'+ranges[1];
545 hl_div.innerHTML = '';
545 hl_div.innerHTML = '';
546 l = document.createElement('a');
546 l = document.createElement('a');
547 l.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
547 l.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
548 l.innerHTML = selection_link_label;
548 l.innerHTML = selection_link_label;
549 hl_div.appendChild(l);
549 hl_div.appendChild(l);
550
550
551 YUD.get('body').appendChild(hl_div);
551 YUD.get('body').appendChild(hl_div);
552
552
553 xy = YUD.getXY(till.id);
553 xy = YUD.getXY(till.id);
554
554
555 YUD.addClass('linktt','yui-tt');
555 YUD.addClass('linktt','yui-tt');
556 YUD.setStyle('linktt','top',xy[1]+offset+'px');
556 YUD.setStyle('linktt','top',xy[1]+offset+'px');
557 YUD.setStyle('linktt','left',xy[0]+'px');
557 YUD.setStyle('linktt','left',xy[0]+'px');
558 YUD.setStyle('linktt','visibility','visible');
558 YUD.setStyle('linktt','visibility','visible');
559 }
559 }
560 else{
560 else{
561 YUD.setStyle('linktt','visibility','hidden');
561 YUD.setStyle('linktt','visibility','hidden');
562 }
562 }
563 }
563 }
564 }
564 }
565 };
565 };
566
567 var deleteNotification = function(url, notification_id){
568 var callback = {
569 success:function(o){
570 var obj = YUD.get(String("notification_"+notification_id));
571 obj.parentNode.removeChild(obj);
572 },
573 failure:function(o){
574 alert("error");
575 },
576 };
577 var postData = '_method=delete';
578 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
579 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
580 callback, postData);
581 };
@@ -1,38 +1,60 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My Notifications')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
5 ${_('My Notifications')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('My Notifications')}
9 ${_('My Notifications')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 <ul class="links">
21 <ul class="links">
22 <li>
22 <li>
23 <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
23 <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
24 </li>
24 </li>
25 </ul>
25 </ul>
26 </div>
26 </div>
27 % if c.notifications:
27 % if c.notifications:
28 <%
29 unread = lambda n:{False:'unread'}.get(n)
30 %>
31 <div class="table">
28 %for notification in c.notifications:
32 %for notification in c.notifications:
29 <div class="table">
33 <div id="notification_${notification.notification_id}">
30 <h4>${notification.subject}</h4>
34 <div class="notification-header">
31 <div>${h.rst(notification.body)}</div>
35 <div class="gravatar">
36 <img alt="gravatar" src="${h.gravatar_url(h.email(notification.created_by_user.email),24)}"/>
37 </div>
38 <div class="desc">
39 <a href="${url('notification', notification_id=notification.notification_id)}">${notification.description}</a>
40 </div>
41 <div class="delete-notifications">
42 <span id="${notification.notification_id}" class="delete-notification delete_icon action"></span>
43 </div>
32 </div>
44 </div>
45 <div class="notification-subject">${h.urlify_text(notification.subject)}</div>
46 </div>
33 %endfor
47 %endfor
48 </div>
34 %else:
49 %else:
35 <div class="table">${_('No notifications here yet')}</div>
50 <div class="table">${_('No notifications here yet')}</div>
36 %endif
51 %endif
37 </div>
52 </div>
53 <script type="text/javascript">
54 var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
55 YUE.on(YUQ('.delete-notification'),'click',function(e){
56 var notification_id = e.currentTarget.id;
57 deleteNotification(url,notification_id)
58 })
59 </script>
38 </%def>
60 </%def>
@@ -1,337 +1,337 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
3
3
4 <!-- HEADER -->
4 <!-- HEADER -->
5 <div id="header">
5 <div id="header">
6 <!-- user -->
6 <!-- user -->
7 <ul id="logged-user">
7 <ul id="logged-user">
8 <li class="first">
8 <li class="first">
9 <div id="quick_login" style="display:none">
9 <div id="quick_login" style="display:none">
10 ${h.form(h.url('login_home',came_from=h.url.current()))}
10 ${h.form(h.url('login_home',came_from=h.url.current()))}
11 <div class="form">
11 <div class="form">
12 <div class="fields">
12 <div class="fields">
13 <div class="field">
13 <div class="field">
14 <div class="label">
14 <div class="label">
15 <label for="username">${_('Username')}:</label>
15 <label for="username">${_('Username')}:</label>
16 </div>
16 </div>
17 <div class="input">
17 <div class="input">
18 ${h.text('username',class_='focus',size=40)}
18 ${h.text('username',class_='focus',size=40)}
19 </div>
19 </div>
20
20
21 </div>
21 </div>
22 <div class="field">
22 <div class="field">
23 <div class="label">
23 <div class="label">
24 <label for="password">${_('Password')}:</label>
24 <label for="password">${_('Password')}:</label>
25 </div>
25 </div>
26 <div class="input">
26 <div class="input">
27 ${h.password('password',class_='focus',size=40)}
27 ${h.password('password',class_='focus',size=40)}
28 </div>
28 </div>
29
29
30 </div>
30 </div>
31 <div class="buttons">
31 <div class="buttons">
32 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
32 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
33 <div class="register">
33 <div class="register">
34 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
34 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
35 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
35 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
36 %endif
36 %endif
37 </div>
37 </div>
38 ${h.submit('sign_in',_('Sign In'),class_="ui-button")}
38 ${h.submit('sign_in',_('Sign In'),class_="ui-button")}
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 ${h.end_form()}
42 ${h.end_form()}
43 </div>
43 </div>
44
44
45 <div class="gravatar">
45 <div class="gravatar">
46 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
46 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
47 </div>
47 </div>
48 <div class="account">
48 <div class="account">
49 %if c.rhodecode_user.username == 'default':
49 %if c.rhodecode_user.username == 'default':
50 <a href="${h.url('public_journal')}">${_('Public journal')}</a>
50 <a href="${h.url('public_journal')}">${_('Public journal')}</a>
51 %else:
51 %else:
52 <div style="float: left">
52 <div style="float: left">
53 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
53 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
54 </div>
54 </div>
55 <div class="notifications">
55 <div class="notifications">
56 <a href="${h.url('admin_settings_notifications')}">${c.unread_notifications}</a>
56 <a href="${h.url('notifications')}">${c.unread_notifications}</a>
57 </div>
57 </div>
58 %endif
58 %endif
59 </div>
59 </div>
60 </li>
60 </li>
61 <li>
61 <li>
62 <a href="${h.url('home')}">${_('Home')}</a>
62 <a href="${h.url('home')}">${_('Home')}</a>
63 </li>
63 </li>
64 %if c.rhodecode_user.username != 'default':
64 %if c.rhodecode_user.username != 'default':
65 <li>
65 <li>
66 <a href="${h.url('journal')}">${_('Journal')}</a>
66 <a href="${h.url('journal')}">${_('Journal')}</a>
67 ##(${c.unread_journal}
67 ##(${c.unread_journal}
68 </li>
68 </li>
69 %endif
69 %endif
70 %if c.rhodecode_user.username == 'default':
70 %if c.rhodecode_user.username == 'default':
71 <li class="last highlight">${h.link_to(_(u'Login'),h.url('login_home'),id='quick_login_link')}</li>
71 <li class="last highlight">${h.link_to(_(u'Login'),h.url('login_home'),id='quick_login_link')}</li>
72 %else:
72 %else:
73 <li class="last highlight">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
73 <li class="last highlight">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
74 %endif
74 %endif
75 </ul>
75 </ul>
76 <!-- end user -->
76 <!-- end user -->
77 <div id="header-inner" class="title">
77 <div id="header-inner" class="title">
78 <div id="logo">
78 <div id="logo">
79 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
79 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
80 </div>
80 </div>
81 <!-- MENU -->
81 <!-- MENU -->
82 ${self.page_nav()}
82 ${self.page_nav()}
83 <!-- END MENU -->
83 <!-- END MENU -->
84 ${self.body()}
84 ${self.body()}
85 </div>
85 </div>
86 </div>
86 </div>
87 <!-- END HEADER -->
87 <!-- END HEADER -->
88
88
89 <!-- CONTENT -->
89 <!-- CONTENT -->
90 <div id="content">
90 <div id="content">
91 <div class="flash_msg">
91 <div class="flash_msg">
92 <% messages = h.flash.pop_messages() %>
92 <% messages = h.flash.pop_messages() %>
93 % if messages:
93 % if messages:
94 <ul id="flash-messages">
94 <ul id="flash-messages">
95 % for message in messages:
95 % for message in messages:
96 <li class="${message.category}_msg">${message}</li>
96 <li class="${message.category}_msg">${message}</li>
97 % endfor
97 % endfor
98 </ul>
98 </ul>
99 % endif
99 % endif
100 </div>
100 </div>
101 <div id="main">
101 <div id="main">
102 ${next.main()}
102 ${next.main()}
103 </div>
103 </div>
104 </div>
104 </div>
105 <!-- END CONTENT -->
105 <!-- END CONTENT -->
106
106
107 <!-- FOOTER -->
107 <!-- FOOTER -->
108 <div id="footer">
108 <div id="footer">
109 <div id="footer-inner" class="title">
109 <div id="footer-inner" class="title">
110 <div>
110 <div>
111 <p class="footer-link">
111 <p class="footer-link">
112 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
112 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
113 </p>
113 </p>
114 <p class="footer-link-right">
114 <p class="footer-link-right">
115 <a href="${h.url('rhodecode_official')}">RhodeCode</a>
115 <a href="${h.url('rhodecode_official')}">RhodeCode</a>
116 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
116 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
117 </p>
117 </p>
118 </div>
118 </div>
119 </div>
119 </div>
120 </div>
120 </div>
121 <!-- END FOOTER -->
121 <!-- END FOOTER -->
122
122
123 ### MAKO DEFS ###
123 ### MAKO DEFS ###
124 <%def name="page_nav()">
124 <%def name="page_nav()">
125 ${self.menu()}
125 ${self.menu()}
126 </%def>
126 </%def>
127
127
128 <%def name="breadcrumbs()">
128 <%def name="breadcrumbs()">
129 <div class="breadcrumbs">
129 <div class="breadcrumbs">
130 ${self.breadcrumbs_links()}
130 ${self.breadcrumbs_links()}
131 </div>
131 </div>
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="menu(current=None)">
135 <%def name="menu(current=None)">
136 <%
136 <%
137 def is_current(selected):
137 def is_current(selected):
138 if selected == current:
138 if selected == current:
139 return h.literal('class="current"')
139 return h.literal('class="current"')
140 %>
140 %>
141 %if current not in ['home','admin']:
141 %if current not in ['home','admin']:
142 ##REGULAR MENU
142 ##REGULAR MENU
143 <ul id="quick">
143 <ul id="quick">
144 <!-- repo switcher -->
144 <!-- repo switcher -->
145 <li>
145 <li>
146 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
146 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
147 <span class="icon">
147 <span class="icon">
148 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
148 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
149 </span>
149 </span>
150 <span>&darr;</span>
150 <span>&darr;</span>
151 </a>
151 </a>
152 <ul id="repo_switcher_list" class="repo_switcher">
152 <ul id="repo_switcher_list" class="repo_switcher">
153 <li>
153 <li>
154 <a href="#">${_('loading...')}</a>
154 <a href="#">${_('loading...')}</a>
155 </li>
155 </li>
156 </ul>
156 </ul>
157 </li>
157 </li>
158
158
159 <li ${is_current('summary')}>
159 <li ${is_current('summary')}>
160 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
160 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
161 <span class="icon">
161 <span class="icon">
162 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
162 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
163 </span>
163 </span>
164 <span>${_('Summary')}</span>
164 <span>${_('Summary')}</span>
165 </a>
165 </a>
166 </li>
166 </li>
167 ##<li ${is_current('shortlog')}>
167 ##<li ${is_current('shortlog')}>
168 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
168 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
169 ## <span class="icon">
169 ## <span class="icon">
170 ## <img src="${h.url('/images/icons/application_view_list.png')}" alt="${_('Shortlog')}" />
170 ## <img src="${h.url('/images/icons/application_view_list.png')}" alt="${_('Shortlog')}" />
171 ## </span>
171 ## </span>
172 ## <span>${_('Shortlog')}</span>
172 ## <span>${_('Shortlog')}</span>
173 ## </a>
173 ## </a>
174 ##</li>
174 ##</li>
175 <li ${is_current('changelog')}>
175 <li ${is_current('changelog')}>
176 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
176 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
177 <span class="icon">
177 <span class="icon">
178 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
178 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
179 </span>
179 </span>
180 <span>${_('Changelog')}</span>
180 <span>${_('Changelog')}</span>
181 </a>
181 </a>
182 </li>
182 </li>
183
183
184 <li ${is_current('switch_to')}>
184 <li ${is_current('switch_to')}>
185 <a id="branch_tag_switcher" title="${_('Switch to')}" href="#">
185 <a id="branch_tag_switcher" title="${_('Switch to')}" href="#">
186 <span class="icon">
186 <span class="icon">
187 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
187 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
188 </span>
188 </span>
189 <span>${_('Switch to')}</span>
189 <span>${_('Switch to')}</span>
190 </a>
190 </a>
191 <ul id="switch_to_list" class="switch_to">
191 <ul id="switch_to_list" class="switch_to">
192 <li><a href="#">${_('loading...')}</a></li>
192 <li><a href="#">${_('loading...')}</a></li>
193 </ul>
193 </ul>
194 </li>
194 </li>
195 <li ${is_current('files')}>
195 <li ${is_current('files')}>
196 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
196 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
197 <span class="icon">
197 <span class="icon">
198 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
198 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
199 </span>
199 </span>
200 <span>${_('Files')}</span>
200 <span>${_('Files')}</span>
201 </a>
201 </a>
202 </li>
202 </li>
203
203
204 <li ${is_current('options')}>
204 <li ${is_current('options')}>
205 <a title="${_('Options')}" href="#">
205 <a title="${_('Options')}" href="#">
206 <span class="icon">
206 <span class="icon">
207 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
207 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
208 </span>
208 </span>
209 <span>${_('Options')}</span>
209 <span>${_('Options')}</span>
210 </a>
210 </a>
211 <ul>
211 <ul>
212 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
212 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
213 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
213 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
214 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
214 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
215 %else:
215 %else:
216 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
216 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
217 %endif
217 %endif
218 %endif
218 %endif
219 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
219 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
220 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
220 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
221
221
222 % if h.HasPermissionAll('hg.admin')('access admin main page'):
222 % if h.HasPermissionAll('hg.admin')('access admin main page'):
223 <li>
223 <li>
224 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
224 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
225 <%def name="admin_menu()">
225 <%def name="admin_menu()">
226 <ul>
226 <ul>
227 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
227 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
228 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
228 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
229 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
229 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
230 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
230 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
231 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
231 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
232 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
232 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
233 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
233 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
234 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
234 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
235 </ul>
235 </ul>
236 </%def>
236 </%def>
237
237
238 ${admin_menu()}
238 ${admin_menu()}
239 </li>
239 </li>
240 % endif
240 % endif
241 </ul>
241 </ul>
242 </li>
242 </li>
243
243
244 <li>
244 <li>
245 <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
245 <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
246 <span class="icon_short">
246 <span class="icon_short">
247 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
247 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
248 </span>
248 </span>
249 <span id="current_followers_count" class="short">${c.repository_followers}</span>
249 <span id="current_followers_count" class="short">${c.repository_followers}</span>
250 </a>
250 </a>
251 </li>
251 </li>
252 <li>
252 <li>
253 <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
253 <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
254 <span class="icon_short">
254 <span class="icon_short">
255 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
255 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
256 </span>
256 </span>
257 <span class="short">${c.repository_forks}</span>
257 <span class="short">${c.repository_forks}</span>
258 </a>
258 </a>
259 </li>
259 </li>
260 </ul>
260 </ul>
261 <script type="text/javascript">
261 <script type="text/javascript">
262 YUE.on('repo_switcher','mouseover',function(){
262 YUE.on('repo_switcher','mouseover',function(){
263 function qfilter(){
263 function qfilter(){
264 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
264 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
265 var target = 'q_filter_rs';
265 var target = 'q_filter_rs';
266 var func = function(node){
266 var func = function(node){
267 return node.parentNode;
267 return node.parentNode;
268 }
268 }
269 q_filter(target,nodes,func);
269 q_filter(target,nodes,func);
270 }
270 }
271 var loaded = YUD.hasClass('repo_switcher','loaded');
271 var loaded = YUD.hasClass('repo_switcher','loaded');
272 if(!loaded){
272 if(!loaded){
273 YUD.addClass('repo_switcher','loaded');
273 YUD.addClass('repo_switcher','loaded');
274 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
274 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
275 function(o){qfilter();},
275 function(o){qfilter();},
276 function(o){YUD.removeClass('repo_switcher','loaded');}
276 function(o){YUD.removeClass('repo_switcher','loaded');}
277 ,null);
277 ,null);
278 }
278 }
279 return false;
279 return false;
280 });
280 });
281
281
282 YUE.on('branch_tag_switcher','mouseover',function(){
282 YUE.on('branch_tag_switcher','mouseover',function(){
283 var loaded = YUD.hasClass('branch_tag_switcher','loaded');
283 var loaded = YUD.hasClass('branch_tag_switcher','loaded');
284 if(!loaded){
284 if(!loaded){
285 YUD.addClass('branch_tag_switcher','loaded');
285 YUD.addClass('branch_tag_switcher','loaded');
286 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list',
286 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list',
287 function(o){},
287 function(o){},
288 function(o){YUD.removeClass('branch_tag_switcher','loaded');}
288 function(o){YUD.removeClass('branch_tag_switcher','loaded');}
289 ,null);
289 ,null);
290 }
290 }
291 return false;
291 return false;
292 });
292 });
293 </script>
293 </script>
294 %else:
294 %else:
295 ##ROOT MENU
295 ##ROOT MENU
296 <ul id="quick">
296 <ul id="quick">
297 <li>
297 <li>
298 <a title="${_('Home')}" href="${h.url('home')}">
298 <a title="${_('Home')}" href="${h.url('home')}">
299 <span class="icon">
299 <span class="icon">
300 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
300 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
301 </span>
301 </span>
302 <span>${_('Home')}</span>
302 <span>${_('Home')}</span>
303 </a>
303 </a>
304 </li>
304 </li>
305 % if c.rhodecode_user.username != 'default':
305 % if c.rhodecode_user.username != 'default':
306 <li>
306 <li>
307 <a title="${_('Journal')}" href="${h.url('journal')}">
307 <a title="${_('Journal')}" href="${h.url('journal')}">
308 <span class="icon">
308 <span class="icon">
309 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
309 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
310 </span>
310 </span>
311 <span>${_('Journal')}</span>
311 <span>${_('Journal')}</span>
312 </a>
312 </a>
313 </li>
313 </li>
314 % endif
314 % endif
315 <li>
315 <li>
316 <a title="${_('Search')}" href="${h.url('search')}">
316 <a title="${_('Search')}" href="${h.url('search')}">
317 <span class="icon">
317 <span class="icon">
318 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
318 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
319 </span>
319 </span>
320 <span>${_('Search')}</span>
320 <span>${_('Search')}</span>
321 </a>
321 </a>
322 </li>
322 </li>
323
323
324 %if h.HasPermissionAll('hg.admin')('access admin main page'):
324 %if h.HasPermissionAll('hg.admin')('access admin main page'):
325 <li ${is_current('admin')}>
325 <li ${is_current('admin')}>
326 <a title="${_('Admin')}" href="${h.url('admin_home')}">
326 <a title="${_('Admin')}" href="${h.url('admin_home')}">
327 <span class="icon">
327 <span class="icon">
328 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
328 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
329 </span>
329 </span>
330 <span>${_('Admin')}</span>
330 <span>${_('Admin')}</span>
331 </a>
331 </a>
332 ${admin_menu()}
332 ${admin_menu()}
333 </li>
333 </li>
334 %endif
334 %endif
335 </ul>
335 </ul>
336 %endif
336 %endif
337 </%def>
337 </%def>
@@ -1,85 +1,92 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 import os
10 import os
11 import time
11 import time
12 import logging
12 from os.path import join as jn
13 from os.path import join as jn
13
14
14 from unittest import TestCase
15 from unittest import TestCase
15
16
16 from paste.deploy import loadapp
17 from paste.deploy import loadapp
17 from paste.script.appinstall import SetupCommand
18 from paste.script.appinstall import SetupCommand
18 from pylons import config, url
19 from pylons import config, url
19 from routes.util import URLGenerator
20 from routes.util import URLGenerator
20 from webtest import TestApp
21 from webtest import TestApp
21
22
22 from rhodecode.model import meta
23 from rhodecode.model import meta
23 import logging
24 from rhodecode.model.db import User
25
24 import pylons.test
26 import pylons.test
25
27
26 os.environ['TZ'] = 'UTC'
28 os.environ['TZ'] = 'UTC'
27 time.tzset()
29 time.tzset()
28
30
29 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
30
32
31 __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
33 __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
32 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
34 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
33 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS' ]
35 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS' ]
34
36
35 # Invoke websetup with the current config file
37 # Invoke websetup with the current config file
36 # SetupCommand('setup-app').run([config_file])
38 # SetupCommand('setup-app').run([config_file])
37
39
38 ##RUNNING DESIRED TESTS
40 ##RUNNING DESIRED TESTS
39 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
41 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
40 # nosetests --pdb --pdb-failures
42 # nosetests --pdb --pdb-failures
41 environ = {}
43 environ = {}
42
44
43 #SOME GLOBALS FOR TESTS
45 #SOME GLOBALS FOR TESTS
44 from tempfile import _RandomNameSequence
46 from tempfile import _RandomNameSequence
45 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
47 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
46 TEST_USER_ADMIN_LOGIN = 'test_admin'
48 TEST_USER_ADMIN_LOGIN = 'test_admin'
47 TEST_USER_ADMIN_PASS = 'test12'
49 TEST_USER_ADMIN_PASS = 'test12'
48 HG_REPO = 'vcs_test_hg'
50 HG_REPO = 'vcs_test_hg'
49 GIT_REPO = 'vcs_test_git'
51 GIT_REPO = 'vcs_test_git'
50
52
51 NEW_HG_REPO = 'vcs_test_hg_new'
53 NEW_HG_REPO = 'vcs_test_hg_new'
52 NEW_GIT_REPO = 'vcs_test_git_new'
54 NEW_GIT_REPO = 'vcs_test_git_new'
53
55
54 HG_FORK = 'vcs_test_hg_fork'
56 HG_FORK = 'vcs_test_hg_fork'
55 GIT_FORK = 'vcs_test_git_fork'
57 GIT_FORK = 'vcs_test_git_fork'
56
58
57 class TestController(TestCase):
59 class TestController(TestCase):
58
60
59 def __init__(self, *args, **kwargs):
61 def __init__(self, *args, **kwargs):
60 wsgiapp = pylons.test.pylonsapp
62 wsgiapp = pylons.test.pylonsapp
61 config = wsgiapp.config
63 config = wsgiapp.config
62
64
63 self.app = TestApp(wsgiapp)
65 self.app = TestApp(wsgiapp)
64 url._push_object(URLGenerator(config['routes.map'], environ))
66 url._push_object(URLGenerator(config['routes.map'], environ))
65 self.sa = meta.Session
67 self.sa = meta.Session
66 self.index_location = config['app_conf']['index_dir']
68 self.index_location = config['app_conf']['index_dir']
67 TestCase.__init__(self, *args, **kwargs)
69 TestCase.__init__(self, *args, **kwargs)
68
70
69 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
71 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
70 password=TEST_USER_ADMIN_PASS):
72 password=TEST_USER_ADMIN_PASS):
73 self._logged_username = username
71 response = self.app.post(url(controller='login', action='index'),
74 response = self.app.post(url(controller='login', action='index'),
72 {'username':username,
75 {'username':username,
73 'password':password})
76 'password':password})
74
77
75 if 'invalid user name' in response.body:
78 if 'invalid user name' in response.body:
76 self.fail('could not login using %s %s' % (username, password))
79 self.fail('could not login using %s %s' % (username, password))
77
80
78 self.assertEqual(response.status, '302 Found')
81 self.assertEqual(response.status, '302 Found')
79 self.assertEqual(response.session['rhodecode_user'].username, username)
82 self.assertEqual(response.session['rhodecode_user'].username, username)
80 return response.follow()
83 return response.follow()
81
84
85 def _get_logged_user(self):
86 return User.get_by_username(self._logged_username)
87
88
82 def checkSessionFlash(self, response, msg):
89 def checkSessionFlash(self, response, msg):
83 self.assertTrue('flash' in response.session)
90 self.assertTrue('flash' in response.session)
84 self.assertTrue(msg in response.session['flash'][0][1])
91 self.assertTrue(msg in response.session['flash'][0][1])
85
92
@@ -1,221 +1,225 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification
8 from sqlalchemy.exc import IntegrityError
8 from sqlalchemy.exc import IntegrityError
9 from rhodecode.model.user import UserModel
9 from rhodecode.model.user import UserModel
10
10
11 from rhodecode.model import meta
11 from rhodecode.model import meta
12
12
13 Session = meta.Session()
13 Session = meta.Session()
14
14
15 class TestReposGroups(unittest.TestCase):
15 class TestReposGroups(unittest.TestCase):
16
16
17 def setUp(self):
17 def setUp(self):
18 self.g1 = self.__make_group('test1', skip_if_exists=True)
18 self.g1 = self.__make_group('test1', skip_if_exists=True)
19 self.g2 = self.__make_group('test2', skip_if_exists=True)
19 self.g2 = self.__make_group('test2', skip_if_exists=True)
20 self.g3 = self.__make_group('test3', skip_if_exists=True)
20 self.g3 = self.__make_group('test3', skip_if_exists=True)
21
21
22 def tearDown(self):
22 def tearDown(self):
23 print 'out'
23 print 'out'
24
24
25 def __check_path(self, *path):
25 def __check_path(self, *path):
26 path = [TESTS_TMP_PATH] + list(path)
26 path = [TESTS_TMP_PATH] + list(path)
27 path = os.path.join(*path)
27 path = os.path.join(*path)
28 return os.path.isdir(path)
28 return os.path.isdir(path)
29
29
30 def _check_folders(self):
30 def _check_folders(self):
31 print os.listdir(TESTS_TMP_PATH)
31 print os.listdir(TESTS_TMP_PATH)
32
32
33 def __make_group(self, path, desc='desc', parent_id=None,
33 def __make_group(self, path, desc='desc', parent_id=None,
34 skip_if_exists=False):
34 skip_if_exists=False):
35
35
36 gr = RepoGroup.get_by_group_name(path)
36 gr = RepoGroup.get_by_group_name(path)
37 if gr and skip_if_exists:
37 if gr and skip_if_exists:
38 return gr
38 return gr
39
39
40 form_data = dict(group_name=path,
40 form_data = dict(group_name=path,
41 group_description=desc,
41 group_description=desc,
42 group_parent_id=parent_id)
42 group_parent_id=parent_id)
43 gr = ReposGroupModel().create(form_data)
43 gr = ReposGroupModel().create(form_data)
44 return gr
44 return gr
45
45
46 def __delete_group(self, id_):
46 def __delete_group(self, id_):
47 ReposGroupModel().delete(id_)
47 ReposGroupModel().delete(id_)
48
48
49
49
50 def __update_group(self, id_, path, desc='desc', parent_id=None):
50 def __update_group(self, id_, path, desc='desc', parent_id=None):
51 form_data = dict(group_name=path,
51 form_data = dict(group_name=path,
52 group_description=desc,
52 group_description=desc,
53 group_parent_id=parent_id)
53 group_parent_id=parent_id)
54
54
55 gr = ReposGroupModel().update(id_, form_data)
55 gr = ReposGroupModel().update(id_, form_data)
56 return gr
56 return gr
57
57
58 def test_create_group(self):
58 def test_create_group(self):
59 g = self.__make_group('newGroup')
59 g = self.__make_group('newGroup')
60 self.assertEqual(g.full_path, 'newGroup')
60 self.assertEqual(g.full_path, 'newGroup')
61
61
62 self.assertTrue(self.__check_path('newGroup'))
62 self.assertTrue(self.__check_path('newGroup'))
63
63
64
64
65 def test_create_same_name_group(self):
65 def test_create_same_name_group(self):
66 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
66 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
67
67
68
68
69 def test_same_subgroup(self):
69 def test_same_subgroup(self):
70 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
70 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
71 self.assertEqual(sg1.parent_group, self.g1)
71 self.assertEqual(sg1.parent_group, self.g1)
72 self.assertEqual(sg1.full_path, 'test1/sub1')
72 self.assertEqual(sg1.full_path, 'test1/sub1')
73 self.assertTrue(self.__check_path('test1', 'sub1'))
73 self.assertTrue(self.__check_path('test1', 'sub1'))
74
74
75 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
75 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
76 self.assertEqual(ssg1.parent_group, sg1)
76 self.assertEqual(ssg1.parent_group, sg1)
77 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
77 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
78 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
78 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
79
79
80
80
81 def test_remove_group(self):
81 def test_remove_group(self):
82 sg1 = self.__make_group('deleteme')
82 sg1 = self.__make_group('deleteme')
83 self.__delete_group(sg1.group_id)
83 self.__delete_group(sg1.group_id)
84
84
85 self.assertEqual(RepoGroup.get(sg1.group_id), None)
85 self.assertEqual(RepoGroup.get(sg1.group_id), None)
86 self.assertFalse(self.__check_path('deteteme'))
86 self.assertFalse(self.__check_path('deteteme'))
87
87
88 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
88 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
89 self.__delete_group(sg1.group_id)
89 self.__delete_group(sg1.group_id)
90
90
91 self.assertEqual(RepoGroup.get(sg1.group_id), None)
91 self.assertEqual(RepoGroup.get(sg1.group_id), None)
92 self.assertFalse(self.__check_path('test1', 'deteteme'))
92 self.assertFalse(self.__check_path('test1', 'deteteme'))
93
93
94
94
95 def test_rename_single_group(self):
95 def test_rename_single_group(self):
96 sg1 = self.__make_group('initial')
96 sg1 = self.__make_group('initial')
97
97
98 new_sg1 = self.__update_group(sg1.group_id, 'after')
98 new_sg1 = self.__update_group(sg1.group_id, 'after')
99 self.assertTrue(self.__check_path('after'))
99 self.assertTrue(self.__check_path('after'))
100 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
100 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
101
101
102
102
103 def test_update_group_parent(self):
103 def test_update_group_parent(self):
104
104
105 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
105 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
106
106
107 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
107 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
108 self.assertTrue(self.__check_path('test1', 'after'))
108 self.assertTrue(self.__check_path('test1', 'after'))
109 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
109 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
110
110
111
111
112 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
112 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
113 self.assertTrue(self.__check_path('test3', 'after'))
113 self.assertTrue(self.__check_path('test3', 'after'))
114 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
114 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
115
115
116
116
117 new_sg1 = self.__update_group(sg1.group_id, 'hello')
117 new_sg1 = self.__update_group(sg1.group_id, 'hello')
118 self.assertTrue(self.__check_path('hello'))
118 self.assertTrue(self.__check_path('hello'))
119
119
120 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
120 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
121
121
122
122
123
123
124 def test_subgrouping_with_repo(self):
124 def test_subgrouping_with_repo(self):
125
125
126 g1 = self.__make_group('g1')
126 g1 = self.__make_group('g1')
127 g2 = self.__make_group('g2')
127 g2 = self.__make_group('g2')
128
128
129 # create new repo
129 # create new repo
130 form_data = dict(repo_name='john',
130 form_data = dict(repo_name='john',
131 repo_name_full='john',
131 repo_name_full='john',
132 fork_name=None,
132 fork_name=None,
133 description=None,
133 description=None,
134 repo_group=None,
134 repo_group=None,
135 private=False,
135 private=False,
136 repo_type='hg',
136 repo_type='hg',
137 clone_uri=None)
137 clone_uri=None)
138 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
138 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
139 r = RepoModel().create(form_data, cur_user)
139 r = RepoModel().create(form_data, cur_user)
140
140
141 self.assertEqual(r.repo_name, 'john')
141 self.assertEqual(r.repo_name, 'john')
142
142
143 # put repo into group
143 # put repo into group
144 form_data = form_data
144 form_data = form_data
145 form_data['repo_group'] = g1.group_id
145 form_data['repo_group'] = g1.group_id
146 form_data['perms_new'] = []
146 form_data['perms_new'] = []
147 form_data['perms_updates'] = []
147 form_data['perms_updates'] = []
148 RepoModel().update(r.repo_name, form_data)
148 RepoModel().update(r.repo_name, form_data)
149 self.assertEqual(r.repo_name, 'g1/john')
149 self.assertEqual(r.repo_name, 'g1/john')
150
150
151
151
152 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
152 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
153 self.assertTrue(self.__check_path('g2', 'g1'))
153 self.assertTrue(self.__check_path('g2', 'g1'))
154
154
155 # test repo
155 # test repo
156 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
156 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
157
157
158
158
159 class TestNotifications(unittest.TestCase):
159 class TestNotifications(unittest.TestCase):
160
160
161
161
162
162
163 def setUp(self):
163 def setUp(self):
164 self.u1 = UserModel().create_or_update(username='u1', password='qweqwe',
164 self.u1 = UserModel().create_or_update(username=u'u1', password=u'qweqwe',
165 email='u1@rhodecode.org',
165 email=u'u1@rhodecode.org',
166 name='u1', lastname='u1')
166 name=u'u1', lastname=u'u1')
167 self.u2 = UserModel().create_or_update(username='u2', password='qweqwe',
167 self.u2 = UserModel().create_or_update(username=u'u2', password=u'qweqwe',
168 email='u2@rhodecode.org',
168 email=u'u2@rhodecode.org',
169 name='u2', lastname='u3')
169 name=u'u2', lastname=u'u3')
170 self.u3 = UserModel().create_or_update(username='u3', password='qweqwe',
170 self.u3 = UserModel().create_or_update(username=u'u3', password=u'qweqwe',
171 email='u3@rhodecode.org',
171 email=u'u3@rhodecode.org',
172 name='u3', lastname='u3')
172 name=u'u3', lastname=u'u3')
173
173 def tearDown(self):
174 User.delete(self.u1.user_id)
175 User.delete(self.u2.user_id)
176 User.delete(self.u3.user_id)
174
177
175
178
176 def test_create_notification(self):
179 def test_create_notification(self):
177 usrs = [self.u1, self.u2]
180 usrs = [self.u1, self.u2]
178 notification = Notification.create(created_by=self.u1,
181 notification = Notification.create(created_by=self.u1,
179 subject='subj', body='hi there',
182 subject=u'subj', body=u'hi there',
180 recipients=usrs)
183 recipients=usrs)
184 Session.commit()
181
185
182 notifications = Session.query(Notification).all()
186
187 notifications = Notification.query().all()
188 self.assertEqual(len(notifications), 1)
189
183 unotification = UserNotification.query()\
190 unotification = UserNotification.query()\
184 .filter(UserNotification.notification == notification).all()
191 .filter(UserNotification.notification == notification).all()
185 self.assertEqual(len(notifications), 1)
192
186 self.assertEqual(notifications[0].recipients, [self.u1, self.u2])
193 self.assertEqual(notifications[0].recipients, [self.u1, self.u2])
187 self.assertEqual(notification.notification_id,
194 self.assertEqual(notification.notification_id,
188 notifications[0].notification_id)
195 notifications[0].notification_id)
189 self.assertEqual(len(unotification), len(usrs))
196 self.assertEqual(len(unotification), len(usrs))
190 self.assertEqual([x.user.user_id for x in unotification],
197 self.assertEqual([x.user.user_id for x in unotification],
191 [x.user_id for x in usrs])
198 [x.user_id for x in usrs])
192
199
193 def test_user_notifications(self):
200 def test_user_notifications(self):
194 notification1 = Notification.create(created_by=self.u1,
201 notification1 = Notification.create(created_by=self.u1,
195 subject='subj', body='hi there',
202 subject=u'subj', body=u'hi there',
196 recipients=[self.u3])
203 recipients=[self.u3])
197 notification2 = Notification.create(created_by=self.u1,
204 notification2 = Notification.create(created_by=self.u1,
198 subject='subj', body='hi there',
205 subject=u'subj', body=u'hi there',
199 recipients=[self.u3])
206 recipients=[self.u3])
200 self.assertEqual(self.u3.notifications, [notification1, notification2])
207 self.assertEqual(self.u3.notifications, [notification1, notification2])
201
208
202 def test_delete_notifications(self):
209 def test_delete_notifications(self):
203 notification = Notification.create(created_by=self.u1,
210 notification = Notification.create(created_by=self.u1,
204 subject='title', body='hi there3',
211 subject=u'title', body=u'hi there3',
205 recipients=[self.u3, self.u1, self.u2])
212 recipients=[self.u3, self.u1, self.u2])
213 Session.commit()
206 notifications = Notification.query().all()
214 notifications = Notification.query().all()
207 self.assertTrue(notification in notifications)
215 self.assertTrue(notification in notifications)
208
216
209 Notification.delete(notification.notification_id)
217 Notification.delete(notification.notification_id)
218 Session.commit()
210
219
211 notifications = Notification.query().all()
220 notifications = Notification.query().all()
212 self.assertFalse(notification in notifications)
221 self.assertFalse(notification in notifications)
213
222
214 un = UserNotification.query().filter(UserNotification.notification
223 un = UserNotification.query().filter(UserNotification.notification
215 == notification).all()
224 == notification).all()
216 self.assertEqual(un, [])
225 self.assertEqual(un, [])
217
218 def tearDown(self):
219 User.delete(self.u1.user_id)
220 User.delete(self.u2.user_id)
221 User.delete(self.u3.user_id)
General Comments 0
You need to be logged in to leave comments. Login now