##// END OF EJS Templates
spelling: chosen
Mads Kiilerich -
r3622:91ff741c beta
parent child Browse files
Show More
@@ -1,735 +1,735 b''
1 .. _setup:
1 .. _setup:
2
2
3 =====
3 =====
4 Setup
4 Setup
5 =====
5 =====
6
6
7
7
8 Setting up RhodeCode
8 Setting up RhodeCode
9 --------------------
9 --------------------
10
10
11 First, you will need to create a RhodeCode configuration file. Run the
11 First, you will need to create a RhodeCode configuration file. Run the
12 following command to do this::
12 following command to do this::
13
13
14 paster make-config RhodeCode production.ini
14 paster make-config RhodeCode production.ini
15
15
16 - This will create the file `production.ini` in the current directory. This
16 - This will create the file `production.ini` in the current directory. This
17 configuration file contains the various settings for RhodeCode, e.g proxy
17 configuration file contains the various settings for RhodeCode, e.g proxy
18 port, email settings, usage of static files, cache, celery settings and
18 port, email settings, usage of static files, cache, celery settings and
19 logging.
19 logging.
20
20
21
21
22 Next, you need to create the databases used by RhodeCode. I recommend that you
22 Next, you need to create the databases used by RhodeCode. I recommend that you
23 use postgresql or sqlite (default). If you choose a database other than the
23 use postgresql or sqlite (default). If you choose a database other than the
24 default ensure you properly adjust the db url in your production.ini
24 default ensure you properly adjust the db url in your production.ini
25 configuration file to use this other database. RhodeCode currently supports
25 configuration file to use this other database. RhodeCode currently supports
26 postgresql, sqlite and mysql databases. Create the database by running
26 postgresql, sqlite and mysql databases. Create the database by running
27 the following command::
27 the following command::
28
28
29 paster setup-rhodecode production.ini
29 paster setup-rhodecode production.ini
30
30
31 This will prompt you for a "root" path. This "root" path is the location where
31 This will prompt you for a "root" path. This "root" path is the location where
32 RhodeCode will store all of its repositories on the current machine. After
32 RhodeCode will store all of its repositories on the current machine. After
33 entering this "root" path ``setup-rhodecode`` will also prompt you for a username
33 entering this "root" path ``setup-rhodecode`` will also prompt you for a username
34 and password for the initial admin account which ``setup-rhodecode`` sets
34 and password for the initial admin account which ``setup-rhodecode`` sets
35 up for you.
35 up for you.
36
36
37 setup process can be fully automated, example for lazy::
37 setup process can be fully automated, example for lazy::
38
38
39 paster setup-rhodecode production.ini --user=marcink --password=secret --email=marcin@rhodecode.org --repos=/home/marcink/my_repos
39 paster setup-rhodecode production.ini --user=marcink --password=secret --email=marcin@rhodecode.org --repos=/home/marcink/my_repos
40
40
41
41
42 - The ``setup-rhodecode`` command will create all of the needed tables and an
42 - The ``setup-rhodecode`` command will create all of the needed tables and an
43 admin account. When choosing a root path you can either use a new empty
43 admin account. When choosing a root path you can either use a new empty
44 location, or a location which already contains existing repositories. If you
44 location, or a location which already contains existing repositories. If you
45 choose a location which contains existing repositories RhodeCode will simply
45 choose a location which contains existing repositories RhodeCode will simply
46 add all of the repositories at the chosen location to it's database.
46 add all of the repositories at the chosen location to it's database.
47 (Note: make sure you specify the correct path to the root).
47 (Note: make sure you specify the correct path to the root).
48 - Note: the given path for mercurial_ repositories **must** be write accessible
48 - Note: the given path for mercurial_ repositories **must** be write accessible
49 for the application. It's very important since the RhodeCode web interface
49 for the application. It's very important since the RhodeCode web interface
50 will work without write access, but when trying to do a push it will
50 will work without write access, but when trying to do a push it will
51 eventually fail with permission denied errors unless it has write access.
51 eventually fail with permission denied errors unless it has write access.
52
52
53 You are now ready to use RhodeCode, to run it simply execute::
53 You are now ready to use RhodeCode, to run it simply execute::
54
54
55 paster serve production.ini
55 paster serve production.ini
56
56
57 - This command runs the RhodeCode server. The web app should be available at the
57 - This command runs the RhodeCode server. The web app should be available at the
58 127.0.0.1:5000. This ip and port is configurable via the production.ini
58 127.0.0.1:5000. This ip and port is configurable via the production.ini
59 file created in previous step
59 file created in previous step
60 - Use the admin account you created above when running ``setup-rhodecode``
60 - Use the admin account you created above when running ``setup-rhodecode``
61 to login to the web app.
61 to login to the web app.
62 - The default permissions on each repository is read, and the owner is admin.
62 - The default permissions on each repository is read, and the owner is admin.
63 Remember to update these if needed.
63 Remember to update these if needed.
64 - In the admin panel you can toggle ldap, anonymous, permissions settings. As
64 - In the admin panel you can toggle ldap, anonymous, permissions settings. As
65 well as edit more advanced options on users and repositories
65 well as edit more advanced options on users and repositories
66
66
67 Optionally users can create `rcextensions` package that extends RhodeCode
67 Optionally users can create `rcextensions` package that extends RhodeCode
68 functionality. To do this simply execute::
68 functionality. To do this simply execute::
69
69
70 paster make-rcext production.ini
70 paster make-rcext production.ini
71
71
72 This will create `rcextensions` package in the same place that your `ini` file
72 This will create `rcextensions` package in the same place that your `ini` file
73 lives. With `rcextensions` it's possible to add additional mapping for whoosh,
73 lives. With `rcextensions` it's possible to add additional mapping for whoosh,
74 stats and add additional code into the push/pull/create/delete repo hooks.
74 stats and add additional code into the push/pull/create/delete repo hooks.
75 For example for sending signals to build-bots such as jenkins.
75 For example for sending signals to build-bots such as jenkins.
76 Please see the `__init__.py` file inside `rcextensions` package
76 Please see the `__init__.py` file inside `rcextensions` package
77 for more details.
77 for more details.
78
78
79
79
80 Using RhodeCode with SSH
80 Using RhodeCode with SSH
81 ------------------------
81 ------------------------
82
82
83 RhodeCode currently only hosts repositories using http and https. (The addition
83 RhodeCode currently only hosts repositories using http and https. (The addition
84 of ssh hosting is a planned future feature.) However you can easily use ssh in
84 of ssh hosting is a planned future feature.) However you can easily use ssh in
85 parallel with RhodeCode. (Repository access via ssh is a standard "out of
85 parallel with RhodeCode. (Repository access via ssh is a standard "out of
86 the box" feature of mercurial_ and you can use this to access any of the
86 the box" feature of mercurial_ and you can use this to access any of the
87 repositories that RhodeCode is hosting. See PublishingRepositories_)
87 repositories that RhodeCode is hosting. See PublishingRepositories_)
88
88
89 RhodeCode repository structures are kept in directories with the same name
89 RhodeCode repository structures are kept in directories with the same name
90 as the project. When using repository groups, each group is a subdirectory.
90 as the project. When using repository groups, each group is a subdirectory.
91 This allows you to easily use ssh for accessing repositories.
91 This allows you to easily use ssh for accessing repositories.
92
92
93 In order to use ssh you need to make sure that your web-server and the users
93 In order to use ssh you need to make sure that your web-server and the users
94 login accounts have the correct permissions set on the appropriate directories.
94 login accounts have the correct permissions set on the appropriate directories.
95 (Note that these permissions are independent of any permissions you have set up
95 (Note that these permissions are independent of any permissions you have set up
96 using the RhodeCode web interface.)
96 using the RhodeCode web interface.)
97
97
98 If your main directory (the same as set in RhodeCode settings) is for example
98 If your main directory (the same as set in RhodeCode settings) is for example
99 set to **/home/hg** and the repository you are using is named `rhodecode`, then
99 set to **/home/hg** and the repository you are using is named `rhodecode`, then
100 to clone via ssh you should run::
100 to clone via ssh you should run::
101
101
102 hg clone ssh://user@server.com/home/hg/rhodecode
102 hg clone ssh://user@server.com/home/hg/rhodecode
103
103
104 Using other external tools such as mercurial-server_ or using ssh key based
104 Using other external tools such as mercurial-server_ or using ssh key based
105 authentication is fully supported.
105 authentication is fully supported.
106
106
107 Note: In an advanced setup, in order for your ssh access to use the same
107 Note: In an advanced setup, in order for your ssh access to use the same
108 permissions as set up via the RhodeCode web interface, you can create an
108 permissions as set up via the RhodeCode web interface, you can create an
109 authentication hook to connect to the rhodecode db and runs check functions for
109 authentication hook to connect to the rhodecode db and runs check functions for
110 permissions against that.
110 permissions against that.
111
111
112 Setting up Whoosh full text search
112 Setting up Whoosh full text search
113 ----------------------------------
113 ----------------------------------
114
114
115 Starting from version 1.1 the whoosh index can be build by using the paster
115 Starting from version 1.1 the whoosh index can be build by using the paster
116 command ``make-index``. To use ``make-index`` you must specify the configuration
116 command ``make-index``. To use ``make-index`` you must specify the configuration
117 file that stores the location of the index. You may specify the location of the
117 file that stores the location of the index. You may specify the location of the
118 repositories (`--repo-location`). If not specified, this value is retrieved
118 repositories (`--repo-location`). If not specified, this value is retrieved
119 from the RhodeCode database. This was required prior to 1.2. Starting from
119 from the RhodeCode database. This was required prior to 1.2. Starting from
120 version 1.2 it is also possible to specify a comma separated list of
120 version 1.2 it is also possible to specify a comma separated list of
121 repositories (`--index-only`) to build index only on chooses repositories
121 repositories (`--index-only`) to build index only on chooses repositories
122 skipping any other found in repos location
122 skipping any other found in repos location
123
123
124 You may optionally pass the option `-f` to enable a full index rebuild. Without
124 You may optionally pass the option `-f` to enable a full index rebuild. Without
125 the `-f` option, indexing will run always in "incremental" mode.
125 the `-f` option, indexing will run always in "incremental" mode.
126
126
127 For an incremental index build use::
127 For an incremental index build use::
128
128
129 paster make-index production.ini
129 paster make-index production.ini
130
130
131 For a full index rebuild use::
131 For a full index rebuild use::
132
132
133 paster make-index production.ini -f
133 paster make-index production.ini -f
134
134
135
135
136 building index just for chosen repositories is possible with such command::
136 building index just for chosen repositories is possible with such command::
137
137
138 paster make-index production.ini --index-only=vcs,rhodecode
138 paster make-index production.ini --index-only=vcs,rhodecode
139
139
140
140
141 In order to do periodical index builds and keep your index always up to date.
141 In order to do periodical index builds and keep your index always up to date.
142 It's recommended to do a crontab entry for incremental indexing.
142 It's recommended to do a crontab entry for incremental indexing.
143 An example entry might look like this::
143 An example entry might look like this::
144
144
145 /path/to/python/bin/paster make-index /path/to/rhodecode/production.ini
145 /path/to/python/bin/paster make-index /path/to/rhodecode/production.ini
146
146
147 When using incremental mode (the default) whoosh will check the last
147 When using incremental mode (the default) whoosh will check the last
148 modification date of each file and add it to be reindexed if a newer file is
148 modification date of each file and add it to be reindexed if a newer file is
149 available. The indexing daemon checks for any removed files and removes them
149 available. The indexing daemon checks for any removed files and removes them
150 from index.
150 from index.
151
151
152 If you want to rebuild index from scratch, you can use the `-f` flag as above,
152 If you want to rebuild index from scratch, you can use the `-f` flag as above,
153 or in the admin panel you can check `build from scratch` flag.
153 or in the admin panel you can check `build from scratch` flag.
154
154
155
155
156 Setting up LDAP support
156 Setting up LDAP support
157 -----------------------
157 -----------------------
158
158
159 RhodeCode starting from version 1.1 supports ldap authentication. In order
159 RhodeCode starting from version 1.1 supports ldap authentication. In order
160 to use LDAP, you have to install the python-ldap_ package. This package is
160 to use LDAP, you have to install the python-ldap_ package. This package is
161 available via pypi, so you can install it by running
161 available via pypi, so you can install it by running
162
162
163 using easy_install::
163 using easy_install::
164
164
165 easy_install python-ldap
165 easy_install python-ldap
166
166
167 using pip::
167 using pip::
168
168
169 pip install python-ldap
169 pip install python-ldap
170
170
171 .. note::
171 .. note::
172 python-ldap requires some certain libs on your system, so before installing
172 python-ldap requires some certain libs on your system, so before installing
173 it check that you have at least `openldap`, and `sasl` libraries.
173 it check that you have at least `openldap`, and `sasl` libraries.
174
174
175 LDAP settings are located in admin->ldap section,
175 LDAP settings are located in admin->ldap section,
176
176
177 Here's a typical ldap setup::
177 Here's a typical ldap setup::
178
178
179 Connection settings
179 Connection settings
180 Enable LDAP = checked
180 Enable LDAP = checked
181 Host = host.example.org
181 Host = host.example.org
182 Port = 389
182 Port = 389
183 Account = <account>
183 Account = <account>
184 Password = <password>
184 Password = <password>
185 Connection Security = LDAPS connection
185 Connection Security = LDAPS connection
186 Certificate Checks = DEMAND
186 Certificate Checks = DEMAND
187
187
188 Search settings
188 Search settings
189 Base DN = CN=users,DC=host,DC=example,DC=org
189 Base DN = CN=users,DC=host,DC=example,DC=org
190 LDAP Filter = (&(objectClass=user)(!(objectClass=computer)))
190 LDAP Filter = (&(objectClass=user)(!(objectClass=computer)))
191 LDAP Search Scope = SUBTREE
191 LDAP Search Scope = SUBTREE
192
192
193 Attribute mappings
193 Attribute mappings
194 Login Attribute = uid
194 Login Attribute = uid
195 First Name Attribute = firstName
195 First Name Attribute = firstName
196 Last Name Attribute = lastName
196 Last Name Attribute = lastName
197 E-mail Attribute = mail
197 E-mail Attribute = mail
198
198
199 .. _enable_ldap:
199 .. _enable_ldap:
200
200
201 Enable LDAP : required
201 Enable LDAP : required
202 Whether to use LDAP for authenticating users.
202 Whether to use LDAP for authenticating users.
203
203
204 .. _ldap_host:
204 .. _ldap_host:
205
205
206 Host : required
206 Host : required
207 LDAP server hostname or IP address. Can be also a comma separated
207 LDAP server hostname or IP address. Can be also a comma separated
208 list of servers to support LDAP fail-over.
208 list of servers to support LDAP fail-over.
209
209
210 .. _Port:
210 .. _Port:
211
211
212 Port : required
212 Port : required
213 389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
213 389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
214
214
215 .. _ldap_account:
215 .. _ldap_account:
216
216
217 Account : optional
217 Account : optional
218 Only required if the LDAP server does not allow anonymous browsing of
218 Only required if the LDAP server does not allow anonymous browsing of
219 records. This should be a special account for record browsing. This
219 records. This should be a special account for record browsing. This
220 will require `LDAP Password`_ below.
220 will require `LDAP Password`_ below.
221
221
222 .. _LDAP Password:
222 .. _LDAP Password:
223
223
224 Password : optional
224 Password : optional
225 Only required if the LDAP server does not allow anonymous browsing of
225 Only required if the LDAP server does not allow anonymous browsing of
226 records.
226 records.
227
227
228 .. _Enable LDAPS:
228 .. _Enable LDAPS:
229
229
230 Connection Security : required
230 Connection Security : required
231 Defines the connection to LDAP server
231 Defines the connection to LDAP server
232
232
233 No encryption
233 No encryption
234 Plain non encrypted connection
234 Plain non encrypted connection
235
235
236 LDAPS connection
236 LDAPS connection
237 Enable ldaps connection. It will likely require `Port`_ to be set to
237 Enable ldaps connection. It will likely require `Port`_ to be set to
238 a different value (standard LDAPS port is 636). When LDAPS is enabled
238 a different value (standard LDAPS port is 636). When LDAPS is enabled
239 then `Certificate Checks`_ is required.
239 then `Certificate Checks`_ is required.
240
240
241 START_TLS on LDAP connection
241 START_TLS on LDAP connection
242 START TLS connection
242 START TLS connection
243
243
244 .. _Certificate Checks:
244 .. _Certificate Checks:
245
245
246 Certificate Checks : optional
246 Certificate Checks : optional
247 How SSL certificates verification is handled - this is only useful when
247 How SSL certificates verification is handled - this is only useful when
248 `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
248 `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
249 while the other options are susceptible to man-in-the-middle attacks. SSL
249 while the other options are susceptible to man-in-the-middle attacks. SSL
250 certificates can be installed to /etc/openldap/cacerts so that the
250 certificates can be installed to /etc/openldap/cacerts so that the
251 DEMAND or HARD options can be used with self-signed certificates or
251 DEMAND or HARD options can be used with self-signed certificates or
252 certificates that do not have traceable certificates of authority.
252 certificates that do not have traceable certificates of authority.
253
253
254 NEVER
254 NEVER
255 A serve certificate will never be requested or checked.
255 A serve certificate will never be requested or checked.
256
256
257 ALLOW
257 ALLOW
258 A server certificate is requested. Failure to provide a
258 A server certificate is requested. Failure to provide a
259 certificate or providing a bad certificate will not terminate the
259 certificate or providing a bad certificate will not terminate the
260 session.
260 session.
261
261
262 TRY
262 TRY
263 A server certificate is requested. Failure to provide a
263 A server certificate is requested. Failure to provide a
264 certificate does not halt the session; providing a bad certificate
264 certificate does not halt the session; providing a bad certificate
265 halts the session.
265 halts the session.
266
266
267 DEMAND
267 DEMAND
268 A server certificate is requested and must be provided and
268 A server certificate is requested and must be provided and
269 authenticated for the session to proceed.
269 authenticated for the session to proceed.
270
270
271 HARD
271 HARD
272 The same as DEMAND.
272 The same as DEMAND.
273
273
274 .. _Base DN:
274 .. _Base DN:
275
275
276 Base DN : required
276 Base DN : required
277 The Distinguished Name (DN) where searches for users will be performed.
277 The Distinguished Name (DN) where searches for users will be performed.
278 Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
278 Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
279
279
280 .. _LDAP Filter:
280 .. _LDAP Filter:
281
281
282 LDAP Filter : optional
282 LDAP Filter : optional
283 A LDAP filter defined by RFC 2254. This is more useful when `LDAP
283 A LDAP filter defined by RFC 2254. This is more useful when `LDAP
284 Search Scope`_ is set to SUBTREE. The filter is useful for limiting
284 Search Scope`_ is set to SUBTREE. The filter is useful for limiting
285 which LDAP objects are identified as representing Users for
285 which LDAP objects are identified as representing Users for
286 authentication. The filter is augmented by `Login Attribute`_ below.
286 authentication. The filter is augmented by `Login Attribute`_ below.
287 This can commonly be left blank.
287 This can commonly be left blank.
288
288
289 .. _LDAP Search Scope:
289 .. _LDAP Search Scope:
290
290
291 LDAP Search Scope : required
291 LDAP Search Scope : required
292 This limits how far LDAP will search for a matching object.
292 This limits how far LDAP will search for a matching object.
293
293
294 BASE
294 BASE
295 Only allows searching of `Base DN`_ and is usually not what you
295 Only allows searching of `Base DN`_ and is usually not what you
296 want.
296 want.
297
297
298 ONELEVEL
298 ONELEVEL
299 Searches all entries under `Base DN`_, but not Base DN itself.
299 Searches all entries under `Base DN`_, but not Base DN itself.
300
300
301 SUBTREE
301 SUBTREE
302 Searches all entries below `Base DN`_, but not Base DN itself.
302 Searches all entries below `Base DN`_, but not Base DN itself.
303 When using SUBTREE `LDAP Filter`_ is useful to limit object
303 When using SUBTREE `LDAP Filter`_ is useful to limit object
304 location.
304 location.
305
305
306 .. _Login Attribute:
306 .. _Login Attribute:
307
307
308 Login Attribute : required
308 Login Attribute : required
309 The LDAP record attribute that will be matched as the USERNAME or
309 The LDAP record attribute that will be matched as the USERNAME or
310 ACCOUNT used to connect to RhodeCode. This will be added to `LDAP
310 ACCOUNT used to connect to RhodeCode. This will be added to `LDAP
311 Filter`_ for locating the User object. If `LDAP Filter`_ is specified as
311 Filter`_ for locating the User object. If `LDAP Filter`_ is specified as
312 "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
312 "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
313 connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
313 connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
314 ::
314 ::
315
315
316 (&(LDAPFILTER)(uid=jsmith))
316 (&(LDAPFILTER)(uid=jsmith))
317
317
318 .. _ldap_attr_firstname:
318 .. _ldap_attr_firstname:
319
319
320 First Name Attribute : required
320 First Name Attribute : required
321 The LDAP record attribute which represents the user's first name.
321 The LDAP record attribute which represents the user's first name.
322
322
323 .. _ldap_attr_lastname:
323 .. _ldap_attr_lastname:
324
324
325 Last Name Attribute : required
325 Last Name Attribute : required
326 The LDAP record attribute which represents the user's last name.
326 The LDAP record attribute which represents the user's last name.
327
327
328 .. _ldap_attr_email:
328 .. _ldap_attr_email:
329
329
330 Email Attribute : required
330 Email Attribute : required
331 The LDAP record attribute which represents the user's email address.
331 The LDAP record attribute which represents the user's email address.
332
332
333 If all data are entered correctly, and python-ldap_ is properly installed
333 If all data are entered correctly, and python-ldap_ is properly installed
334 users should be granted access to RhodeCode with ldap accounts. At this
334 users should be granted access to RhodeCode with ldap accounts. At this
335 time user information is copied from LDAP into the RhodeCode user database.
335 time user information is copied from LDAP into the RhodeCode user database.
336 This means that updates of an LDAP user object may not be reflected as a
336 This means that updates of an LDAP user object may not be reflected as a
337 user update in RhodeCode.
337 user update in RhodeCode.
338
338
339 If You have problems with LDAP access and believe You entered correct
339 If You have problems with LDAP access and believe You entered correct
340 information check out the RhodeCode logs, any error messages sent from LDAP
340 information check out the RhodeCode logs, any error messages sent from LDAP
341 will be saved there.
341 will be saved there.
342
342
343 Active Directory
343 Active Directory
344 ''''''''''''''''
344 ''''''''''''''''
345
345
346 RhodeCode can use Microsoft Active Directory for user authentication. This
346 RhodeCode can use Microsoft Active Directory for user authentication. This
347 is done through an LDAP or LDAPS connection to Active Directory. The
347 is done through an LDAP or LDAPS connection to Active Directory. The
348 following LDAP configuration settings are typical for using Active
348 following LDAP configuration settings are typical for using Active
349 Directory ::
349 Directory ::
350
350
351 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
351 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
352 Login Attribute = sAMAccountName
352 Login Attribute = sAMAccountName
353 First Name Attribute = givenName
353 First Name Attribute = givenName
354 Last Name Attribute = sn
354 Last Name Attribute = sn
355 E-mail Attribute = mail
355 E-mail Attribute = mail
356
356
357 All other LDAP settings will likely be site-specific and should be
357 All other LDAP settings will likely be site-specific and should be
358 appropriately configured.
358 appropriately configured.
359
359
360
360
361 Authentication by container or reverse-proxy
361 Authentication by container or reverse-proxy
362 --------------------------------------------
362 --------------------------------------------
363
363
364 Starting with version 1.3, RhodeCode supports delegating the authentication
364 Starting with version 1.3, RhodeCode supports delegating the authentication
365 of users to its WSGI container, or to a reverse-proxy server through which all
365 of users to its WSGI container, or to a reverse-proxy server through which all
366 clients access the application.
366 clients access the application.
367
367
368 When these authentication methods are enabled in RhodeCode, it uses the
368 When these authentication methods are enabled in RhodeCode, it uses the
369 username that the container/proxy (Apache/Nginx/etc) authenticated and doesn't
369 username that the container/proxy (Apache/Nginx/etc) authenticated and doesn't
370 perform the authentication itself. The authorization, however, is still done by
370 perform the authentication itself. The authorization, however, is still done by
371 RhodeCode according to its settings.
371 RhodeCode according to its settings.
372
372
373 When a user logs in for the first time using these authentication methods,
373 When a user logs in for the first time using these authentication methods,
374 a matching user account is created in RhodeCode with default permissions. An
374 a matching user account is created in RhodeCode with default permissions. An
375 administrator can then modify it using RhodeCode's admin interface.
375 administrator can then modify it using RhodeCode's admin interface.
376 It's also possible for an administrator to create accounts and configure their
376 It's also possible for an administrator to create accounts and configure their
377 permissions before the user logs in for the first time.
377 permissions before the user logs in for the first time.
378
378
379 Container-based authentication
379 Container-based authentication
380 ''''''''''''''''''''''''''''''
380 ''''''''''''''''''''''''''''''
381
381
382 In a container-based authentication setup, RhodeCode reads the user name from
382 In a container-based authentication setup, RhodeCode reads the user name from
383 the ``REMOTE_USER`` server variable provided by the WSGI container.
383 the ``REMOTE_USER`` server variable provided by the WSGI container.
384
384
385 After setting up your container (see `Apache's WSGI config`_), you'd need
385 After setting up your container (see `Apache's WSGI config`_), you'd need
386 to configure it to require authentication on the location configured for
386 to configure it to require authentication on the location configured for
387 RhodeCode.
387 RhodeCode.
388
388
389 In order for RhodeCode to start using the provided username, you should set the
389 In order for RhodeCode to start using the provided username, you should set the
390 following in the [app:main] section of your .ini file::
390 following in the [app:main] section of your .ini file::
391
391
392 container_auth_enabled = true
392 container_auth_enabled = true
393
393
394
394
395 Proxy pass-through authentication
395 Proxy pass-through authentication
396 '''''''''''''''''''''''''''''''''
396 '''''''''''''''''''''''''''''''''
397
397
398 In a proxy pass-through authentication setup, RhodeCode reads the user name
398 In a proxy pass-through authentication setup, RhodeCode reads the user name
399 from the ``X-Forwarded-User`` request header, which should be configured to be
399 from the ``X-Forwarded-User`` request header, which should be configured to be
400 sent by the reverse-proxy server.
400 sent by the reverse-proxy server.
401
401
402 After setting up your proxy solution (see `Apache virtual host reverse proxy example`_,
402 After setting up your proxy solution (see `Apache virtual host reverse proxy example`_,
403 `Apache as subdirectory`_ or `Nginx virtual host example`_), you'd need to
403 `Apache as subdirectory`_ or `Nginx virtual host example`_), you'd need to
404 configure the authentication and add the username in a request header named
404 configure the authentication and add the username in a request header named
405 ``X-Forwarded-User``.
405 ``X-Forwarded-User``.
406
406
407 For example, the following config section for Apache sets a subdirectory in a
407 For example, the following config section for Apache sets a subdirectory in a
408 reverse-proxy setup with basic auth::
408 reverse-proxy setup with basic auth::
409
409
410 <Location /<someprefix> >
410 <Location /<someprefix> >
411 ProxyPass http://127.0.0.1:5000/<someprefix>
411 ProxyPass http://127.0.0.1:5000/<someprefix>
412 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
412 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
413 SetEnvIf X-Url-Scheme https HTTPS=1
413 SetEnvIf X-Url-Scheme https HTTPS=1
414
414
415 AuthType Basic
415 AuthType Basic
416 AuthName "RhodeCode authentication"
416 AuthName "RhodeCode authentication"
417 AuthUserFile /home/web/rhodecode/.htpasswd
417 AuthUserFile /home/web/rhodecode/.htpasswd
418 require valid-user
418 require valid-user
419
419
420 RequestHeader unset X-Forwarded-User
420 RequestHeader unset X-Forwarded-User
421
421
422 RewriteEngine On
422 RewriteEngine On
423 RewriteCond %{LA-U:REMOTE_USER} (.+)
423 RewriteCond %{LA-U:REMOTE_USER} (.+)
424 RewriteRule .* - [E=RU:%1]
424 RewriteRule .* - [E=RU:%1]
425 RequestHeader set X-Forwarded-User %{RU}e
425 RequestHeader set X-Forwarded-User %{RU}e
426 </Location>
426 </Location>
427
427
428 In order for RhodeCode to start using the forwarded username, you should set
428 In order for RhodeCode to start using the forwarded username, you should set
429 the following in the [app:main] section of your .ini file::
429 the following in the [app:main] section of your .ini file::
430
430
431 proxypass_auth_enabled = true
431 proxypass_auth_enabled = true
432
432
433 .. note::
433 .. note::
434 If you enable proxy pass-through authentication, make sure your server is
434 If you enable proxy pass-through authentication, make sure your server is
435 only accessible through the proxy. Otherwise, any client would be able to
435 only accessible through the proxy. Otherwise, any client would be able to
436 forge the authentication header and could effectively become authenticated
436 forge the authentication header and could effectively become authenticated
437 using any account of their liking.
437 using any account of their liking.
438
438
439 Integration with Issue trackers
439 Integration with Issue trackers
440 -------------------------------
440 -------------------------------
441
441
442 RhodeCode provides a simple integration with issue trackers. It's possible
442 RhodeCode provides a simple integration with issue trackers. It's possible
443 to define a regular expression that will fetch issue id stored in commit
443 to define a regular expression that will fetch issue id stored in commit
444 messages and replace that with an url to this issue. To enable this simply
444 messages and replace that with an url to this issue. To enable this simply
445 uncomment following variables in the ini file::
445 uncomment following variables in the ini file::
446
446
447 url_pat = (?:^#|\s#)(\w+)
447 url_pat = (?:^#|\s#)(\w+)
448 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
448 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
449 issue_prefix = #
449 issue_prefix = #
450
450
451 `url_pat` is the regular expression that will fetch issues from commit messages.
451 `url_pat` is the regular expression that will fetch issues from commit messages.
452 Default regex will match issues in format of #<number> eg. #300.
452 Default regex will match issues in format of #<number> eg. #300.
453
453
454 Matched issues will be replace with the link specified as `issue_server_link`
454 Matched issues will be replace with the link specified as `issue_server_link`
455 {id} will be replaced with issue id, and {repo} with repository name.
455 {id} will be replaced with issue id, and {repo} with repository name.
456 Since the # is striped `issue_prefix` is added as a prefix to url.
456 Since the # is striped `issue_prefix` is added as a prefix to url.
457 `issue_prefix` can be something different than # if you pass
457 `issue_prefix` can be something different than # if you pass
458 ISSUE- as issue prefix this will generate an url in format::
458 ISSUE- as issue prefix this will generate an url in format::
459
459
460 <a href="https://myissueserver.com/example_repo/issue/300">ISSUE-300</a>
460 <a href="https://myissueserver.com/example_repo/issue/300">ISSUE-300</a>
461
461
462 Hook management
462 Hook management
463 ---------------
463 ---------------
464
464
465 Hooks can be managed in similar way to this used in .hgrc files.
465 Hooks can be managed in similar way to this used in .hgrc files.
466 To access hooks setting click `advanced setup` on Hooks section of Mercurial
466 To access hooks setting click `advanced setup` on Hooks section of Mercurial
467 Settings in Admin.
467 Settings in Admin.
468
468
469 There are 4 built in hooks that cannot be changed (only enable/disable by
469 There are 4 built in hooks that cannot be changed (only enable/disable by
470 checkboxes on previos section).
470 checkboxes on previos section).
471 To add another custom hook simply fill in first section with
471 To add another custom hook simply fill in first section with
472 <name>.<hook_type> and the second one with hook path. Example hooks
472 <name>.<hook_type> and the second one with hook path. Example hooks
473 can be found at *rhodecode.lib.hooks*.
473 can be found at *rhodecode.lib.hooks*.
474
474
475
475
476 Changing default encoding
476 Changing default encoding
477 -------------------------
477 -------------------------
478
478
479 By default RhodeCode uses utf8 encoding, starting from 1.3 series this
479 By default RhodeCode uses utf8 encoding, starting from 1.3 series this
480 can be changed, simply edit default_encoding in .ini file to desired one.
480 can be changed, simply edit default_encoding in .ini file to desired one.
481 This affects many parts in rhodecode including committers names, filenames,
481 This affects many parts in rhodecode including committers names, filenames,
482 encoding of commit messages. In addition RhodeCode can detect if `chardet`
482 encoding of commit messages. In addition RhodeCode can detect if `chardet`
483 library is installed. If `chardet` is detected RhodeCode will fallback to it
483 library is installed. If `chardet` is detected RhodeCode will fallback to it
484 when there are encode/decode errors.
484 when there are encode/decode errors.
485
485
486
486
487 Setting Up Celery
487 Setting Up Celery
488 -----------------
488 -----------------
489
489
490 Since version 1.1 celery is configured by the rhodecode ini configuration files.
490 Since version 1.1 celery is configured by the rhodecode ini configuration files.
491 Simply set use_celery=true in the ini file then add / change the configuration
491 Simply set use_celery=true in the ini file then add / change the configuration
492 variables inside the ini file.
492 variables inside the ini file.
493
493
494 Remember that the ini files use the format with '.' not with '_' like celery.
494 Remember that the ini files use the format with '.' not with '_' like celery.
495 So for example setting `BROKER_HOST` in celery means setting `broker.host` in
495 So for example setting `BROKER_HOST` in celery means setting `broker.host` in
496 the config file.
496 the config file.
497
497
498 In order to start using celery run::
498 In order to start using celery run::
499
499
500 paster celeryd <configfile.ini>
500 paster celeryd <configfile.ini>
501
501
502
502
503 .. note::
503 .. note::
504 Make sure you run this command from the same virtualenv, and with the same
504 Make sure you run this command from the same virtualenv, and with the same
505 user that rhodecode runs.
505 user that rhodecode runs.
506
506
507 HTTPS support
507 HTTPS support
508 -------------
508 -------------
509
509
510 There are two ways to enable https:
510 There are two ways to enable https:
511
511
512 - Set HTTP_X_URL_SCHEME in your http server headers, than rhodecode will
512 - Set HTTP_X_URL_SCHEME in your http server headers, than rhodecode will
513 recognize this headers and make proper https redirections
513 recognize this headers and make proper https redirections
514 - Alternatively, change the `force_https = true` flag in the ini configuration
514 - Alternatively, change the `force_https = true` flag in the ini configuration
515 to force using https, no headers are needed than to enable https
515 to force using https, no headers are needed than to enable https
516
516
517
517
518 Nginx virtual host example
518 Nginx virtual host example
519 --------------------------
519 --------------------------
520
520
521 Sample config for nginx using proxy::
521 Sample config for nginx using proxy::
522
522
523 upstream rc {
523 upstream rc {
524 server 127.0.0.1:5000;
524 server 127.0.0.1:5000;
525 # add more instances for load balancing
525 # add more instances for load balancing
526 #server 127.0.0.1:5001;
526 #server 127.0.0.1:5001;
527 #server 127.0.0.1:5002;
527 #server 127.0.0.1:5002;
528 }
528 }
529
529
530 server {
530 server {
531 listen 443;
531 listen 443;
532 server_name rhodecode.myserver.com;
532 server_name rhodecode.myserver.com;
533 access_log /var/log/nginx/rhodecode.access.log;
533 access_log /var/log/nginx/rhodecode.access.log;
534 error_log /var/log/nginx/rhodecode.error.log;
534 error_log /var/log/nginx/rhodecode.error.log;
535
535
536 ssl on;
536 ssl on;
537 ssl_certificate rhodecode.myserver.com.crt;
537 ssl_certificate rhodecode.myserver.com.crt;
538 ssl_certificate_key rhodecode.myserver.com.key;
538 ssl_certificate_key rhodecode.myserver.com.key;
539
539
540 ssl_session_timeout 5m;
540 ssl_session_timeout 5m;
541
541
542 ssl_protocols SSLv3 TLSv1;
542 ssl_protocols SSLv3 TLSv1;
543 ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
543 ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
544 ssl_prefer_server_ciphers on;
544 ssl_prefer_server_ciphers on;
545
545
546 # uncomment if you have nginx with chunking module compiled
546 # uncomment if you have nginx with chunking module compiled
547 # fixes the issues of having to put postBuffer data for large git
547 # fixes the issues of having to put postBuffer data for large git
548 # pushes
548 # pushes
549 #chunkin on;
549 #chunkin on;
550 #error_page 411 = @my_411_error;
550 #error_page 411 = @my_411_error;
551 #location @my_411_error {
551 #location @my_411_error {
552 # chunkin_resume;
552 # chunkin_resume;
553 #}
553 #}
554
554
555 # uncomment if you want to serve static files by nginx
555 # uncomment if you want to serve static files by nginx
556 #root /path/to/installation/rhodecode/public;
556 #root /path/to/installation/rhodecode/public;
557
557
558 location / {
558 location / {
559 try_files $uri @rhode;
559 try_files $uri @rhode;
560 }
560 }
561
561
562 location @rhode {
562 location @rhode {
563 proxy_pass http://rc;
563 proxy_pass http://rc;
564 include /etc/nginx/proxy.conf;
564 include /etc/nginx/proxy.conf;
565 }
565 }
566
566
567 }
567 }
568
568
569 Here's the proxy.conf. It's tuned so it will not timeout on long
569 Here's the proxy.conf. It's tuned so it will not timeout on long
570 pushes or large pushes::
570 pushes or large pushes::
571
571
572 proxy_redirect off;
572 proxy_redirect off;
573 proxy_set_header Host $host;
573 proxy_set_header Host $host;
574 proxy_set_header X-Url-Scheme $scheme;
574 proxy_set_header X-Url-Scheme $scheme;
575 proxy_set_header X-Host $http_host;
575 proxy_set_header X-Host $http_host;
576 proxy_set_header X-Real-IP $remote_addr;
576 proxy_set_header X-Real-IP $remote_addr;
577 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
577 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
578 proxy_set_header Proxy-host $proxy_host;
578 proxy_set_header Proxy-host $proxy_host;
579 client_max_body_size 400m;
579 client_max_body_size 400m;
580 client_body_buffer_size 128k;
580 client_body_buffer_size 128k;
581 proxy_buffering off;
581 proxy_buffering off;
582 proxy_connect_timeout 7200;
582 proxy_connect_timeout 7200;
583 proxy_send_timeout 7200;
583 proxy_send_timeout 7200;
584 proxy_read_timeout 7200;
584 proxy_read_timeout 7200;
585 proxy_buffers 8 32k;
585 proxy_buffers 8 32k;
586
586
587 Also, when using root path with nginx you might set the static files to false
587 Also, when using root path with nginx you might set the static files to false
588 in the production.ini file::
588 in the production.ini file::
589
589
590 [app:main]
590 [app:main]
591 use = egg:rhodecode
591 use = egg:rhodecode
592 full_stack = true
592 full_stack = true
593 static_files = false
593 static_files = false
594 lang=en
594 lang=en
595 cache_dir = %(here)s/data
595 cache_dir = %(here)s/data
596
596
597 In order to not have the statics served by the application. This improves speed.
597 In order to not have the statics served by the application. This improves speed.
598
598
599
599
600 Apache virtual host reverse proxy example
600 Apache virtual host reverse proxy example
601 -----------------------------------------
601 -----------------------------------------
602
602
603 Here is a sample configuration file for apache using proxy::
603 Here is a sample configuration file for apache using proxy::
604
604
605 <VirtualHost *:80>
605 <VirtualHost *:80>
606 ServerName hg.myserver.com
606 ServerName hg.myserver.com
607 ServerAlias hg.myserver.com
607 ServerAlias hg.myserver.com
608
608
609 <Proxy *>
609 <Proxy *>
610 Order allow,deny
610 Order allow,deny
611 Allow from all
611 Allow from all
612 </Proxy>
612 </Proxy>
613
613
614 #important !
614 #important !
615 #Directive to properly generate url (clone url) for pylons
615 #Directive to properly generate url (clone url) for pylons
616 ProxyPreserveHost On
616 ProxyPreserveHost On
617
617
618 #rhodecode instance
618 #rhodecode instance
619 ProxyPass / http://127.0.0.1:5000/
619 ProxyPass / http://127.0.0.1:5000/
620 ProxyPassReverse / http://127.0.0.1:5000/
620 ProxyPassReverse / http://127.0.0.1:5000/
621
621
622 #to enable https use line below
622 #to enable https use line below
623 #SetEnvIf X-Url-Scheme https HTTPS=1
623 #SetEnvIf X-Url-Scheme https HTTPS=1
624
624
625 </VirtualHost>
625 </VirtualHost>
626
626
627
627
628 Additional tutorial
628 Additional tutorial
629 http://wiki.pylonshq.com/display/pylonscookbook/Apache+as+a+reverse+proxy+for+Pylons
629 http://wiki.pylonshq.com/display/pylonscookbook/Apache+as+a+reverse+proxy+for+Pylons
630
630
631
631
632 Apache as subdirectory
632 Apache as subdirectory
633 ----------------------
633 ----------------------
634
634
635 Apache subdirectory part::
635 Apache subdirectory part::
636
636
637 <Location /<someprefix> >
637 <Location /<someprefix> >
638 ProxyPass http://127.0.0.1:5000/<someprefix>
638 ProxyPass http://127.0.0.1:5000/<someprefix>
639 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
639 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
640 SetEnvIf X-Url-Scheme https HTTPS=1
640 SetEnvIf X-Url-Scheme https HTTPS=1
641 </Location>
641 </Location>
642
642
643 Besides the regular apache setup you will need to add the following line
643 Besides the regular apache setup you will need to add the following line
644 into [app:main] section of your .ini file::
644 into [app:main] section of your .ini file::
645
645
646 filter-with = proxy-prefix
646 filter-with = proxy-prefix
647
647
648 Add the following at the end of the .ini file::
648 Add the following at the end of the .ini file::
649
649
650 [filter:proxy-prefix]
650 [filter:proxy-prefix]
651 use = egg:PasteDeploy#prefix
651 use = egg:PasteDeploy#prefix
652 prefix = /<someprefix>
652 prefix = /<someprefix>
653
653
654
654
655 then change <someprefix> into your choosen prefix
655 then change <someprefix> into your chosen prefix
656
656
657 Apache's WSGI config
657 Apache's WSGI config
658 --------------------
658 --------------------
659
659
660 Alternatively, RhodeCode can be set up with Apache under mod_wsgi. For
660 Alternatively, RhodeCode can be set up with Apache under mod_wsgi. For
661 that, you'll need to:
661 that, you'll need to:
662
662
663 - Install mod_wsgi. If using a Debian-based distro, you can install
663 - Install mod_wsgi. If using a Debian-based distro, you can install
664 the package libapache2-mod-wsgi::
664 the package libapache2-mod-wsgi::
665
665
666 aptitude install libapache2-mod-wsgi
666 aptitude install libapache2-mod-wsgi
667
667
668 - Enable mod_wsgi::
668 - Enable mod_wsgi::
669
669
670 a2enmod wsgi
670 a2enmod wsgi
671
671
672 - Create a wsgi dispatch script, like the one below. Make sure you
672 - Create a wsgi dispatch script, like the one below. Make sure you
673 check the paths correctly point to where you installed RhodeCode
673 check the paths correctly point to where you installed RhodeCode
674 and its Python Virtual Environment.
674 and its Python Virtual Environment.
675 - Enable the WSGIScriptAlias directive for the wsgi dispatch script,
675 - Enable the WSGIScriptAlias directive for the wsgi dispatch script,
676 as in the following example. Once again, check the paths are
676 as in the following example. Once again, check the paths are
677 correctly specified.
677 correctly specified.
678
678
679 Here is a sample excerpt from an Apache Virtual Host configuration file::
679 Here is a sample excerpt from an Apache Virtual Host configuration file::
680
680
681 WSGIDaemonProcess pylons \
681 WSGIDaemonProcess pylons \
682 threads=4 \
682 threads=4 \
683 python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
683 python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
684 WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
684 WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
685 WSGIPassAuthorization On
685 WSGIPassAuthorization On
686
686
687 .. note::
687 .. note::
688 when running apache as root please add: `user=www-data group=www-data`
688 when running apache as root please add: `user=www-data group=www-data`
689 into above configuration
689 into above configuration
690
690
691 .. note::
691 .. note::
692 Running RhodeCode in multiprocess mode in apache is not supported,
692 Running RhodeCode in multiprocess mode in apache is not supported,
693 make sure you don't specify `processes=num` directive in the config
693 make sure you don't specify `processes=num` directive in the config
694
694
695
695
696 Example wsgi dispatch script::
696 Example wsgi dispatch script::
697
697
698 import os
698 import os
699 os.environ["HGENCODING"] = "UTF-8"
699 os.environ["HGENCODING"] = "UTF-8"
700 os.environ['PYTHON_EGG_CACHE'] = '/home/web/rhodecode/.egg-cache'
700 os.environ['PYTHON_EGG_CACHE'] = '/home/web/rhodecode/.egg-cache'
701
701
702 # sometimes it's needed to set the curent dir
702 # sometimes it's needed to set the curent dir
703 os.chdir('/home/web/rhodecode/')
703 os.chdir('/home/web/rhodecode/')
704
704
705 import site
705 import site
706 site.addsitedir("/home/web/rhodecode/pyenv/lib/python2.6/site-packages")
706 site.addsitedir("/home/web/rhodecode/pyenv/lib/python2.6/site-packages")
707
707
708 from paste.deploy import loadapp
708 from paste.deploy import loadapp
709 from paste.script.util.logging_config import fileConfig
709 from paste.script.util.logging_config import fileConfig
710
710
711 fileConfig('/home/web/rhodecode/production.ini')
711 fileConfig('/home/web/rhodecode/production.ini')
712 application = loadapp('config:/home/web/rhodecode/production.ini')
712 application = loadapp('config:/home/web/rhodecode/production.ini')
713
713
714 Note: when using mod_wsgi you'll need to install the same version of
714 Note: when using mod_wsgi you'll need to install the same version of
715 Mercurial that's inside RhodeCode's virtualenv also on the system's Python
715 Mercurial that's inside RhodeCode's virtualenv also on the system's Python
716 environment.
716 environment.
717
717
718
718
719 Other configuration files
719 Other configuration files
720 -------------------------
720 -------------------------
721
721
722 Some example init.d scripts can be found in init.d directory::
722 Some example init.d scripts can be found in init.d directory::
723
723
724 https://secure.rhodecode.org/rhodecode/files/beta/init.d
724 https://secure.rhodecode.org/rhodecode/files/beta/init.d
725
725
726 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
726 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
727 .. _python: http://www.python.org/
727 .. _python: http://www.python.org/
728 .. _mercurial: http://mercurial.selenic.com/
728 .. _mercurial: http://mercurial.selenic.com/
729 .. _celery: http://celeryproject.org/
729 .. _celery: http://celeryproject.org/
730 .. _rabbitmq: http://www.rabbitmq.com/
730 .. _rabbitmq: http://www.rabbitmq.com/
731 .. _python-ldap: http://www.python-ldap.org/
731 .. _python-ldap: http://www.python-ldap.org/
732 .. _mercurial-server: http://www.lshift.net/mercurial-server.html
732 .. _mercurial-server: http://www.lshift.net/mercurial-server.html
733 .. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
733 .. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
734 .. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
734 .. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
735 .. _google group rhodecode: http://groups.google.com/group/rhodecode
735 .. _google group rhodecode: http://groups.google.com/group/rhodecode
@@ -1,2175 +1,2175 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 String.prototype.strip = function(char) {
47 String.prototype.strip = function(char) {
48 if(char === undefined){
48 if(char === undefined){
49 char = '\\s';
49 char = '\\s';
50 }
50 }
51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
52 }
52 }
53 String.prototype.lstrip = function(char) {
53 String.prototype.lstrip = function(char) {
54 if(char === undefined){
54 if(char === undefined){
55 char = '\\s';
55 char = '\\s';
56 }
56 }
57 return this.replace(new RegExp('^'+char+'+'),'');
57 return this.replace(new RegExp('^'+char+'+'),'');
58 }
58 }
59 String.prototype.rstrip = function(char) {
59 String.prototype.rstrip = function(char) {
60 if(char === undefined){
60 if(char === undefined){
61 char = '\\s';
61 char = '\\s';
62 }
62 }
63 return this.replace(new RegExp(''+char+'+$'),'');
63 return this.replace(new RegExp(''+char+'+$'),'');
64 }
64 }
65
65
66
66
67 if(!Array.prototype.indexOf) {
67 if(!Array.prototype.indexOf) {
68 Array.prototype.indexOf = function(needle) {
68 Array.prototype.indexOf = function(needle) {
69 for(var i = 0; i < this.length; i++) {
69 for(var i = 0; i < this.length; i++) {
70 if(this[i] === needle) {
70 if(this[i] === needle) {
71 return i;
71 return i;
72 }
72 }
73 }
73 }
74 return -1;
74 return -1;
75 };
75 };
76 }
76 }
77
77
78 // IE(CRAP) doesn't support previousElementSibling
78 // IE(CRAP) doesn't support previousElementSibling
79 var prevElementSibling = function( el ) {
79 var prevElementSibling = function( el ) {
80 if( el.previousElementSibling ) {
80 if( el.previousElementSibling ) {
81 return el.previousElementSibling;
81 return el.previousElementSibling;
82 } else {
82 } else {
83 while( el = el.previousSibling ) {
83 while( el = el.previousSibling ) {
84 if( el.nodeType === 1 ) return el;
84 if( el.nodeType === 1 ) return el;
85 }
85 }
86 }
86 }
87 }
87 }
88
88
89 /**
89 /**
90 * SmartColorGenerator
90 * SmartColorGenerator
91 *
91 *
92 *usage::
92 *usage::
93 * var CG = new ColorGenerator();
93 * var CG = new ColorGenerator();
94 * var col = CG.getColor(key); //returns array of RGB
94 * var col = CG.getColor(key); //returns array of RGB
95 * 'rgb({0})'.format(col.join(',')
95 * 'rgb({0})'.format(col.join(',')
96 *
96 *
97 * @returns {ColorGenerator}
97 * @returns {ColorGenerator}
98 */
98 */
99 var ColorGenerator = function(){
99 var ColorGenerator = function(){
100 this.GOLDEN_RATIO = 0.618033988749895;
100 this.GOLDEN_RATIO = 0.618033988749895;
101 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
101 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
102 this.HSV_1 = 0.75;//saturation
102 this.HSV_1 = 0.75;//saturation
103 this.HSV_2 = 0.95;
103 this.HSV_2 = 0.95;
104 this.color;
104 this.color;
105 this.cacheColorMap = {};
105 this.cacheColorMap = {};
106 };
106 };
107
107
108 ColorGenerator.prototype = {
108 ColorGenerator.prototype = {
109 getColor:function(key){
109 getColor:function(key){
110 if(this.cacheColorMap[key] !== undefined){
110 if(this.cacheColorMap[key] !== undefined){
111 return this.cacheColorMap[key];
111 return this.cacheColorMap[key];
112 }
112 }
113 else{
113 else{
114 this.cacheColorMap[key] = this.generateColor();
114 this.cacheColorMap[key] = this.generateColor();
115 return this.cacheColorMap[key];
115 return this.cacheColorMap[key];
116 }
116 }
117 },
117 },
118 _hsvToRgb:function(h,s,v){
118 _hsvToRgb:function(h,s,v){
119 if (s == 0.0)
119 if (s == 0.0)
120 return [v, v, v];
120 return [v, v, v];
121 i = parseInt(h * 6.0)
121 i = parseInt(h * 6.0)
122 f = (h * 6.0) - i
122 f = (h * 6.0) - i
123 p = v * (1.0 - s)
123 p = v * (1.0 - s)
124 q = v * (1.0 - s * f)
124 q = v * (1.0 - s * f)
125 t = v * (1.0 - s * (1.0 - f))
125 t = v * (1.0 - s * (1.0 - f))
126 i = i % 6
126 i = i % 6
127 if (i == 0)
127 if (i == 0)
128 return [v, t, p]
128 return [v, t, p]
129 if (i == 1)
129 if (i == 1)
130 return [q, v, p]
130 return [q, v, p]
131 if (i == 2)
131 if (i == 2)
132 return [p, v, t]
132 return [p, v, t]
133 if (i == 3)
133 if (i == 3)
134 return [p, q, v]
134 return [p, q, v]
135 if (i == 4)
135 if (i == 4)
136 return [t, p, v]
136 return [t, p, v]
137 if (i == 5)
137 if (i == 5)
138 return [v, p, q]
138 return [v, p, q]
139 },
139 },
140 generateColor:function(){
140 generateColor:function(){
141 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
141 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
142 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
142 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
143 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
143 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
144 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
144 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
145 function toRgb(v){
145 function toRgb(v){
146 return ""+parseInt(v*256)
146 return ""+parseInt(v*256)
147 }
147 }
148 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
148 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
149
149
150 }
150 }
151 }
151 }
152
152
153 /**
153 /**
154 * PyRoutesJS
154 * PyRoutesJS
155 *
155 *
156 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
156 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
157 */
157 */
158 var pyroutes = (function() {
158 var pyroutes = (function() {
159 // access global map defined in special file pyroutes
159 // access global map defined in special file pyroutes
160 var matchlist = PROUTES_MAP;
160 var matchlist = PROUTES_MAP;
161 var sprintf = (function() {
161 var sprintf = (function() {
162 function get_type(variable) {
162 function get_type(variable) {
163 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
163 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
164 }
164 }
165 function str_repeat(input, multiplier) {
165 function str_repeat(input, multiplier) {
166 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
166 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
167 return output.join('');
167 return output.join('');
168 }
168 }
169
169
170 var str_format = function() {
170 var str_format = function() {
171 if (!str_format.cache.hasOwnProperty(arguments[0])) {
171 if (!str_format.cache.hasOwnProperty(arguments[0])) {
172 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
172 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
173 }
173 }
174 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
174 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
175 };
175 };
176
176
177 str_format.format = function(parse_tree, argv) {
177 str_format.format = function(parse_tree, argv) {
178 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
178 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
179 for (i = 0; i < tree_length; i++) {
179 for (i = 0; i < tree_length; i++) {
180 node_type = get_type(parse_tree[i]);
180 node_type = get_type(parse_tree[i]);
181 if (node_type === 'string') {
181 if (node_type === 'string') {
182 output.push(parse_tree[i]);
182 output.push(parse_tree[i]);
183 }
183 }
184 else if (node_type === 'array') {
184 else if (node_type === 'array') {
185 match = parse_tree[i]; // convenience purposes only
185 match = parse_tree[i]; // convenience purposes only
186 if (match[2]) { // keyword argument
186 if (match[2]) { // keyword argument
187 arg = argv[cursor];
187 arg = argv[cursor];
188 for (k = 0; k < match[2].length; k++) {
188 for (k = 0; k < match[2].length; k++) {
189 if (!arg.hasOwnProperty(match[2][k])) {
189 if (!arg.hasOwnProperty(match[2][k])) {
190 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
190 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
191 }
191 }
192 arg = arg[match[2][k]];
192 arg = arg[match[2][k]];
193 }
193 }
194 }
194 }
195 else if (match[1]) { // positional argument (explicit)
195 else if (match[1]) { // positional argument (explicit)
196 arg = argv[match[1]];
196 arg = argv[match[1]];
197 }
197 }
198 else { // positional argument (implicit)
198 else { // positional argument (implicit)
199 arg = argv[cursor++];
199 arg = argv[cursor++];
200 }
200 }
201
201
202 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
202 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
203 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
203 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
204 }
204 }
205 switch (match[8]) {
205 switch (match[8]) {
206 case 'b': arg = arg.toString(2); break;
206 case 'b': arg = arg.toString(2); break;
207 case 'c': arg = String.fromCharCode(arg); break;
207 case 'c': arg = String.fromCharCode(arg); break;
208 case 'd': arg = parseInt(arg, 10); break;
208 case 'd': arg = parseInt(arg, 10); break;
209 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
209 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
210 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
210 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
211 case 'o': arg = arg.toString(8); break;
211 case 'o': arg = arg.toString(8); break;
212 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
212 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
213 case 'u': arg = Math.abs(arg); break;
213 case 'u': arg = Math.abs(arg); break;
214 case 'x': arg = arg.toString(16); break;
214 case 'x': arg = arg.toString(16); break;
215 case 'X': arg = arg.toString(16).toUpperCase(); break;
215 case 'X': arg = arg.toString(16).toUpperCase(); break;
216 }
216 }
217 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
217 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
218 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
218 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
219 pad_length = match[6] - String(arg).length;
219 pad_length = match[6] - String(arg).length;
220 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
220 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
221 output.push(match[5] ? arg + pad : pad + arg);
221 output.push(match[5] ? arg + pad : pad + arg);
222 }
222 }
223 }
223 }
224 return output.join('');
224 return output.join('');
225 };
225 };
226
226
227 str_format.cache = {};
227 str_format.cache = {};
228
228
229 str_format.parse = function(fmt) {
229 str_format.parse = function(fmt) {
230 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
230 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
231 while (_fmt) {
231 while (_fmt) {
232 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
232 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
233 parse_tree.push(match[0]);
233 parse_tree.push(match[0]);
234 }
234 }
235 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
235 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
236 parse_tree.push('%');
236 parse_tree.push('%');
237 }
237 }
238 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
238 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
239 if (match[2]) {
239 if (match[2]) {
240 arg_names |= 1;
240 arg_names |= 1;
241 var field_list = [], replacement_field = match[2], field_match = [];
241 var field_list = [], replacement_field = match[2], field_match = [];
242 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
242 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
243 field_list.push(field_match[1]);
243 field_list.push(field_match[1]);
244 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
244 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
245 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
245 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
246 field_list.push(field_match[1]);
246 field_list.push(field_match[1]);
247 }
247 }
248 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
248 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
249 field_list.push(field_match[1]);
249 field_list.push(field_match[1]);
250 }
250 }
251 else {
251 else {
252 throw('[sprintf] huh?');
252 throw('[sprintf] huh?');
253 }
253 }
254 }
254 }
255 }
255 }
256 else {
256 else {
257 throw('[sprintf] huh?');
257 throw('[sprintf] huh?');
258 }
258 }
259 match[2] = field_list;
259 match[2] = field_list;
260 }
260 }
261 else {
261 else {
262 arg_names |= 2;
262 arg_names |= 2;
263 }
263 }
264 if (arg_names === 3) {
264 if (arg_names === 3) {
265 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
265 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
266 }
266 }
267 parse_tree.push(match);
267 parse_tree.push(match);
268 }
268 }
269 else {
269 else {
270 throw('[sprintf] huh?');
270 throw('[sprintf] huh?');
271 }
271 }
272 _fmt = _fmt.substring(match[0].length);
272 _fmt = _fmt.substring(match[0].length);
273 }
273 }
274 return parse_tree;
274 return parse_tree;
275 };
275 };
276
276
277 return str_format;
277 return str_format;
278 })();
278 })();
279
279
280 var vsprintf = function(fmt, argv) {
280 var vsprintf = function(fmt, argv) {
281 argv.unshift(fmt);
281 argv.unshift(fmt);
282 return sprintf.apply(null, argv);
282 return sprintf.apply(null, argv);
283 };
283 };
284 return {
284 return {
285 'url': function(route_name, params) {
285 'url': function(route_name, params) {
286 var result = route_name;
286 var result = route_name;
287 if (typeof(params) != 'object'){
287 if (typeof(params) != 'object'){
288 params = {};
288 params = {};
289 }
289 }
290 if (matchlist.hasOwnProperty(route_name)) {
290 if (matchlist.hasOwnProperty(route_name)) {
291 var route = matchlist[route_name];
291 var route = matchlist[route_name];
292 // param substitution
292 // param substitution
293 for(var i=0; i < route[1].length; i++) {
293 for(var i=0; i < route[1].length; i++) {
294
294
295 if (!params.hasOwnProperty(route[1][i]))
295 if (!params.hasOwnProperty(route[1][i]))
296 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
296 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
297 }
297 }
298 result = sprintf(route[0], params);
298 result = sprintf(route[0], params);
299
299
300 var ret = [];
300 var ret = [];
301 //extra params => GET
301 //extra params => GET
302 for(param in params){
302 for(param in params){
303 if (route[1].indexOf(param) == -1){
303 if (route[1].indexOf(param) == -1){
304 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
304 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
305 }
305 }
306 }
306 }
307 var _parts = ret.join("&");
307 var _parts = ret.join("&");
308 if(_parts){
308 if(_parts){
309 result = result +'?'+ _parts
309 result = result +'?'+ _parts
310 }
310 }
311 }
311 }
312
312
313 return result;
313 return result;
314 },
314 },
315 'register': function(route_name, route_tmpl, req_params) {
315 'register': function(route_name, route_tmpl, req_params) {
316 if (typeof(req_params) != 'object') {
316 if (typeof(req_params) != 'object') {
317 req_params = [];
317 req_params = [];
318 }
318 }
319 //fix escape
319 //fix escape
320 route_tmpl = unescape(route_tmpl);
320 route_tmpl = unescape(route_tmpl);
321 keys = [];
321 keys = [];
322 for (o in req_params){
322 for (o in req_params){
323 keys.push(req_params[o])
323 keys.push(req_params[o])
324 }
324 }
325 matchlist[route_name] = [
325 matchlist[route_name] = [
326 route_tmpl,
326 route_tmpl,
327 keys
327 keys
328 ]
328 ]
329 },
329 },
330 '_routes': function(){
330 '_routes': function(){
331 return matchlist;
331 return matchlist;
332 }
332 }
333 }
333 }
334 })();
334 })();
335
335
336
336
337
337
338 /**
338 /**
339 * GLOBAL YUI Shortcuts
339 * GLOBAL YUI Shortcuts
340 */
340 */
341 var YUC = YAHOO.util.Connect;
341 var YUC = YAHOO.util.Connect;
342 var YUD = YAHOO.util.Dom;
342 var YUD = YAHOO.util.Dom;
343 var YUE = YAHOO.util.Event;
343 var YUE = YAHOO.util.Event;
344 var YUQ = YAHOO.util.Selector.query;
344 var YUQ = YAHOO.util.Selector.query;
345
345
346 // defines if push state is enabled for this browser ?
346 // defines if push state is enabled for this browser ?
347 var push_state_enabled = Boolean(
347 var push_state_enabled = Boolean(
348 window.history && window.history.pushState && window.history.replaceState
348 window.history && window.history.pushState && window.history.replaceState
349 && !( /* disable for versions of iOS before version 4.3 (8F190) */
349 && !( /* disable for versions of iOS before version 4.3 (8F190) */
350 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
350 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
351 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
351 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
352 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
352 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
353 )
353 )
354 );
354 );
355
355
356 var _run_callbacks = function(callbacks){
356 var _run_callbacks = function(callbacks){
357 if (callbacks !== undefined){
357 if (callbacks !== undefined){
358 var _l = callbacks.length;
358 var _l = callbacks.length;
359 for (var i=0;i<_l;i++){
359 for (var i=0;i<_l;i++){
360 var func = callbacks[i];
360 var func = callbacks[i];
361 if(typeof(func)=='function'){
361 if(typeof(func)=='function'){
362 try{
362 try{
363 func();
363 func();
364 }catch (err){};
364 }catch (err){};
365 }
365 }
366 }
366 }
367 }
367 }
368 }
368 }
369
369
370 /**
370 /**
371 * Partial Ajax Implementation
371 * Partial Ajax Implementation
372 *
372 *
373 * @param url: defines url to make partial request
373 * @param url: defines url to make partial request
374 * @param container: defines id of container to input partial result
374 * @param container: defines id of container to input partial result
375 * @param s_call: success callback function that takes o as arg
375 * @param s_call: success callback function that takes o as arg
376 * o.tId
376 * o.tId
377 * o.status
377 * o.status
378 * o.statusText
378 * o.statusText
379 * o.getResponseHeader[ ]
379 * o.getResponseHeader[ ]
380 * o.getAllResponseHeaders
380 * o.getAllResponseHeaders
381 * o.responseText
381 * o.responseText
382 * o.responseXML
382 * o.responseXML
383 * o.argument
383 * o.argument
384 * @param f_call: failure callback
384 * @param f_call: failure callback
385 * @param args arguments
385 * @param args arguments
386 */
386 */
387 function ypjax(url,container,s_call,f_call,args){
387 function ypjax(url,container,s_call,f_call,args){
388 var method='GET';
388 var method='GET';
389 if(args===undefined){
389 if(args===undefined){
390 args=null;
390 args=null;
391 }
391 }
392
392
393 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
393 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
394 YUC.initHeader('X-PARTIAL-XHR',true);
394 YUC.initHeader('X-PARTIAL-XHR',true);
395
395
396 // wrapper of passed callback
396 // wrapper of passed callback
397 var s_wrapper = (function(o){
397 var s_wrapper = (function(o){
398 return function(o){
398 return function(o){
399 YUD.get(container).innerHTML=o.responseText;
399 YUD.get(container).innerHTML=o.responseText;
400 YUD.setStyle(container,'opacity','1.0');
400 YUD.setStyle(container,'opacity','1.0');
401 //execute the given original callback
401 //execute the given original callback
402 if (s_call !== undefined){
402 if (s_call !== undefined){
403 s_call(o);
403 s_call(o);
404 }
404 }
405 }
405 }
406 })()
406 })()
407 YUD.setStyle(container,'opacity','0.3');
407 YUD.setStyle(container,'opacity','0.3');
408 YUC.asyncRequest(method,url,{
408 YUC.asyncRequest(method,url,{
409 success:s_wrapper,
409 success:s_wrapper,
410 failure:function(o){
410 failure:function(o){
411 console.log(o);
411 console.log(o);
412 YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
412 YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
413 YUD.setStyle(container,'opacity','1.0');
413 YUD.setStyle(container,'opacity','1.0');
414 },
414 },
415 cache:false
415 cache:false
416 },args);
416 },args);
417
417
418 };
418 };
419
419
420 var ajaxGET = function(url,success) {
420 var ajaxGET = function(url,success) {
421 // Set special header for ajax == HTTP_X_PARTIAL_XHR
421 // Set special header for ajax == HTTP_X_PARTIAL_XHR
422 YUC.initHeader('X-PARTIAL-XHR',true);
422 YUC.initHeader('X-PARTIAL-XHR',true);
423
423
424 var sUrl = url;
424 var sUrl = url;
425 var callback = {
425 var callback = {
426 success: success,
426 success: success,
427 failure: function (o) {
427 failure: function (o) {
428 if (o.status != 0) {
428 if (o.status != 0) {
429 alert("error: " + o.statusText);
429 alert("error: " + o.statusText);
430 };
430 };
431 },
431 },
432 };
432 };
433
433
434 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
434 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
435 return request;
435 return request;
436 };
436 };
437
437
438
438
439
439
440 var ajaxPOST = function(url,postData,success) {
440 var ajaxPOST = function(url,postData,success) {
441 // Set special header for ajax == HTTP_X_PARTIAL_XHR
441 // Set special header for ajax == HTTP_X_PARTIAL_XHR
442 YUC.initHeader('X-PARTIAL-XHR',true);
442 YUC.initHeader('X-PARTIAL-XHR',true);
443
443
444 var toQueryString = function(o) {
444 var toQueryString = function(o) {
445 if(typeof o !== 'object') {
445 if(typeof o !== 'object') {
446 return false;
446 return false;
447 }
447 }
448 var _p, _qs = [];
448 var _p, _qs = [];
449 for(_p in o) {
449 for(_p in o) {
450 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
450 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
451 }
451 }
452 return _qs.join('&');
452 return _qs.join('&');
453 };
453 };
454
454
455 var sUrl = url;
455 var sUrl = url;
456 var callback = {
456 var callback = {
457 success: success,
457 success: success,
458 failure: function (o) {
458 failure: function (o) {
459 alert("error");
459 alert("error");
460 },
460 },
461 };
461 };
462 var postData = toQueryString(postData);
462 var postData = toQueryString(postData);
463 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
463 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
464 return request;
464 return request;
465 };
465 };
466
466
467
467
468 /**
468 /**
469 * tooltip activate
469 * tooltip activate
470 */
470 */
471 var tooltip_activate = function(){
471 var tooltip_activate = function(){
472 yt = YAHOO.yuitip.main;
472 yt = YAHOO.yuitip.main;
473 YUE.onDOMReady(yt.init);
473 YUE.onDOMReady(yt.init);
474 };
474 };
475
475
476 /**
476 /**
477 * show more
477 * show more
478 */
478 */
479 var show_more_event = function(){
479 var show_more_event = function(){
480 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
480 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
481 var el = e.target;
481 var el = e.target;
482 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
482 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
483 YUD.setStyle(el.parentNode,'display','none');
483 YUD.setStyle(el.parentNode,'display','none');
484 });
484 });
485 };
485 };
486
486
487 /**
487 /**
488 * show changeset tooltip
488 * show changeset tooltip
489 */
489 */
490 var show_changeset_tooltip = function(){
490 var show_changeset_tooltip = function(){
491 YUE.on(YUD.getElementsByClassName('lazy-cs'), 'mouseover', function(e){
491 YUE.on(YUD.getElementsByClassName('lazy-cs'), 'mouseover', function(e){
492 var target = e.currentTarget;
492 var target = e.currentTarget;
493 var rid = YUD.getAttribute(target,'raw_id');
493 var rid = YUD.getAttribute(target,'raw_id');
494 var repo_name = YUD.getAttribute(target,'repo_name');
494 var repo_name = YUD.getAttribute(target,'repo_name');
495 var ttid = 'tt-'+rid;
495 var ttid = 'tt-'+rid;
496 var success = function(o){
496 var success = function(o){
497 var json = JSON.parse(o.responseText);
497 var json = JSON.parse(o.responseText);
498 YUD.addClass(target,'tooltip')
498 YUD.addClass(target,'tooltip')
499 YUD.setAttribute(target, 'title',json['message']);
499 YUD.setAttribute(target, 'title',json['message']);
500 YAHOO.yuitip.main.show_yuitip(e, target);
500 YAHOO.yuitip.main.show_yuitip(e, target);
501 }
501 }
502 if(rid && !YUD.hasClass(target, 'tooltip')){
502 if(rid && !YUD.hasClass(target, 'tooltip')){
503 YUD.setAttribute(target,'id',ttid);
503 YUD.setAttribute(target,'id',ttid);
504 YUD.setAttribute(target, 'title',_TM['loading...']);
504 YUD.setAttribute(target, 'title',_TM['loading...']);
505 YAHOO.yuitip.main.set_listeners(target);
505 YAHOO.yuitip.main.set_listeners(target);
506 YAHOO.yuitip.main.show_yuitip(e, target);
506 YAHOO.yuitip.main.show_yuitip(e, target);
507 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
507 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
508 ajaxGET(url, success)
508 ajaxGET(url, success)
509 }
509 }
510 });
510 });
511 };
511 };
512
512
513 var onSuccessFollow = function(target){
513 var onSuccessFollow = function(target){
514 var f = YUD.get(target);
514 var f = YUD.get(target);
515 var f_cnt = YUD.get('current_followers_count');
515 var f_cnt = YUD.get('current_followers_count');
516
516
517 if(YUD.hasClass(f, 'follow')){
517 if(YUD.hasClass(f, 'follow')){
518 f.setAttribute('class','following');
518 f.setAttribute('class','following');
519 f.setAttribute('title',_TM['Stop following this repository']);
519 f.setAttribute('title',_TM['Stop following this repository']);
520
520
521 if(f_cnt){
521 if(f_cnt){
522 var cnt = Number(f_cnt.innerHTML)+1;
522 var cnt = Number(f_cnt.innerHTML)+1;
523 f_cnt.innerHTML = cnt;
523 f_cnt.innerHTML = cnt;
524 }
524 }
525 }
525 }
526 else{
526 else{
527 f.setAttribute('class','follow');
527 f.setAttribute('class','follow');
528 f.setAttribute('title',_TM['Start following this repository']);
528 f.setAttribute('title',_TM['Start following this repository']);
529 if(f_cnt){
529 if(f_cnt){
530 var cnt = Number(f_cnt.innerHTML)-1;
530 var cnt = Number(f_cnt.innerHTML)-1;
531 f_cnt.innerHTML = cnt;
531 f_cnt.innerHTML = cnt;
532 }
532 }
533 }
533 }
534 }
534 }
535
535
536 var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
536 var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
537 args = 'follows_user_id='+fallows_user_id;
537 args = 'follows_user_id='+fallows_user_id;
538 args+= '&amp;auth_token='+token;
538 args+= '&amp;auth_token='+token;
539 if(user_id != undefined){
539 if(user_id != undefined){
540 args+="&amp;user_id="+user_id;
540 args+="&amp;user_id="+user_id;
541 }
541 }
542 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
542 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
543 success:function(o){
543 success:function(o){
544 onSuccessFollow(target);
544 onSuccessFollow(target);
545 }
545 }
546 },args);
546 },args);
547 return false;
547 return false;
548 }
548 }
549
549
550 var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
550 var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
551
551
552 args = 'follows_repo_id='+fallows_repo_id;
552 args = 'follows_repo_id='+fallows_repo_id;
553 args+= '&amp;auth_token='+token;
553 args+= '&amp;auth_token='+token;
554 if(user_id != undefined){
554 if(user_id != undefined){
555 args+="&amp;user_id="+user_id;
555 args+="&amp;user_id="+user_id;
556 }
556 }
557 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
557 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
558 success:function(o){
558 success:function(o){
559 onSuccessFollow(target);
559 onSuccessFollow(target);
560 }
560 }
561 },args);
561 },args);
562 return false;
562 return false;
563 }
563 }
564
564
565 var showRepoSize = function(target, repo_name, token){
565 var showRepoSize = function(target, repo_name, token){
566 var args= 'auth_token='+token;
566 var args= 'auth_token='+token;
567
567
568 if(!YUD.hasClass(target, 'loaded')){
568 if(!YUD.hasClass(target, 'loaded')){
569 YUD.get(target).innerHTML = _TM['Loading ...'];
569 YUD.get(target).innerHTML = _TM['Loading ...'];
570 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
570 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
571 YUC.asyncRequest('POST',url,{
571 YUC.asyncRequest('POST',url,{
572 success:function(o){
572 success:function(o){
573 YUD.get(target).innerHTML = JSON.parse(o.responseText);
573 YUD.get(target).innerHTML = JSON.parse(o.responseText);
574 YUD.addClass(target, 'loaded');
574 YUD.addClass(target, 'loaded');
575 }
575 }
576 },args);
576 },args);
577 }
577 }
578 return false;
578 return false;
579 }
579 }
580
580
581 /**
581 /**
582 * TOOLTIP IMPL.
582 * TOOLTIP IMPL.
583 */
583 */
584 YAHOO.namespace('yuitip');
584 YAHOO.namespace('yuitip');
585 YAHOO.yuitip.main = {
585 YAHOO.yuitip.main = {
586
586
587 $: YAHOO.util.Dom.get,
587 $: YAHOO.util.Dom.get,
588
588
589 bgColor: '#000',
589 bgColor: '#000',
590 speed: 0.3,
590 speed: 0.3,
591 opacity: 0.9,
591 opacity: 0.9,
592 offset: [15,15],
592 offset: [15,15],
593 useAnim: false,
593 useAnim: false,
594 maxWidth: 600,
594 maxWidth: 600,
595 add_links: false,
595 add_links: false,
596 yuitips: [],
596 yuitips: [],
597
597
598 set_listeners: function(tt){
598 set_listeners: function(tt){
599 YUE.on(tt, 'mouseover', yt.show_yuitip, tt);
599 YUE.on(tt, 'mouseover', yt.show_yuitip, tt);
600 YUE.on(tt, 'mousemove', yt.move_yuitip, tt);
600 YUE.on(tt, 'mousemove', yt.move_yuitip, tt);
601 YUE.on(tt, 'mouseout', yt.close_yuitip, tt);
601 YUE.on(tt, 'mouseout', yt.close_yuitip, tt);
602 },
602 },
603
603
604 init: function(){
604 init: function(){
605 yt.tipBox = yt.$('tip-box');
605 yt.tipBox = yt.$('tip-box');
606 if(!yt.tipBox){
606 if(!yt.tipBox){
607 yt.tipBox = document.createElement('div');
607 yt.tipBox = document.createElement('div');
608 document.body.appendChild(yt.tipBox);
608 document.body.appendChild(yt.tipBox);
609 yt.tipBox.id = 'tip-box';
609 yt.tipBox.id = 'tip-box';
610 }
610 }
611
611
612 YUD.setStyle(yt.tipBox, 'display', 'none');
612 YUD.setStyle(yt.tipBox, 'display', 'none');
613 YUD.setStyle(yt.tipBox, 'position', 'absolute');
613 YUD.setStyle(yt.tipBox, 'position', 'absolute');
614 if(yt.maxWidth !== null){
614 if(yt.maxWidth !== null){
615 YUD.setStyle(yt.tipBox, 'max-width', yt.maxWidth+'px');
615 YUD.setStyle(yt.tipBox, 'max-width', yt.maxWidth+'px');
616 }
616 }
617
617
618 var yuitips = YUD.getElementsByClassName('tooltip');
618 var yuitips = YUD.getElementsByClassName('tooltip');
619
619
620 if(yt.add_links === true){
620 if(yt.add_links === true){
621 var links = document.getElementsByTagName('a');
621 var links = document.getElementsByTagName('a');
622 var linkLen = links.length;
622 var linkLen = links.length;
623 for(i=0;i<linkLen;i++){
623 for(i=0;i<linkLen;i++){
624 yuitips.push(links[i]);
624 yuitips.push(links[i]);
625 }
625 }
626 }
626 }
627
627
628 var yuiLen = yuitips.length;
628 var yuiLen = yuitips.length;
629
629
630 for(i=0;i<yuiLen;i++){
630 for(i=0;i<yuiLen;i++){
631 yt.set_listeners(yuitips[i]);
631 yt.set_listeners(yuitips[i]);
632 }
632 }
633 },
633 },
634
634
635 show_yuitip: function(e, el){
635 show_yuitip: function(e, el){
636 YUE.stopEvent(e);
636 YUE.stopEvent(e);
637 if(el.tagName.toLowerCase() === 'img'){
637 if(el.tagName.toLowerCase() === 'img'){
638 yt.tipText = el.alt ? el.alt : '';
638 yt.tipText = el.alt ? el.alt : '';
639 } else {
639 } else {
640 yt.tipText = el.title ? el.title : '';
640 yt.tipText = el.title ? el.title : '';
641 }
641 }
642
642
643 if(yt.tipText !== ''){
643 if(yt.tipText !== ''){
644 // save org title
644 // save org title
645 YUD.setAttribute(el, 'tt_title', yt.tipText);
645 YUD.setAttribute(el, 'tt_title', yt.tipText);
646 // reset title to not show org tooltips
646 // reset title to not show org tooltips
647 YUD.setAttribute(el, 'title', '');
647 YUD.setAttribute(el, 'title', '');
648
648
649 yt.tipBox.innerHTML = yt.tipText;
649 yt.tipBox.innerHTML = yt.tipText;
650 YUD.setStyle(yt.tipBox, 'display', 'block');
650 YUD.setStyle(yt.tipBox, 'display', 'block');
651 if(yt.useAnim === true){
651 if(yt.useAnim === true){
652 YUD.setStyle(yt.tipBox, 'opacity', '0');
652 YUD.setStyle(yt.tipBox, 'opacity', '0');
653 var newAnim = new YAHOO.util.Anim(yt.tipBox,
653 var newAnim = new YAHOO.util.Anim(yt.tipBox,
654 {
654 {
655 opacity: { to: yt.opacity }
655 opacity: { to: yt.opacity }
656 }, yt.speed, YAHOO.util.Easing.easeOut
656 }, yt.speed, YAHOO.util.Easing.easeOut
657 );
657 );
658 newAnim.animate();
658 newAnim.animate();
659 }
659 }
660 }
660 }
661 },
661 },
662
662
663 move_yuitip: function(e, el){
663 move_yuitip: function(e, el){
664 YUE.stopEvent(e);
664 YUE.stopEvent(e);
665 var movePos = YUE.getXY(e);
665 var movePos = YUE.getXY(e);
666 YUD.setStyle(yt.tipBox, 'top', (movePos[1] + yt.offset[1]) + 'px');
666 YUD.setStyle(yt.tipBox, 'top', (movePos[1] + yt.offset[1]) + 'px');
667 YUD.setStyle(yt.tipBox, 'left', (movePos[0] + yt.offset[0]) + 'px');
667 YUD.setStyle(yt.tipBox, 'left', (movePos[0] + yt.offset[0]) + 'px');
668 },
668 },
669
669
670 close_yuitip: function(e, el){
670 close_yuitip: function(e, el){
671 YUE.stopEvent(e);
671 YUE.stopEvent(e);
672
672
673 if(yt.useAnim === true){
673 if(yt.useAnim === true){
674 var newAnim = new YAHOO.util.Anim(yt.tipBox,
674 var newAnim = new YAHOO.util.Anim(yt.tipBox,
675 {
675 {
676 opacity: { to: 0 }
676 opacity: { to: 0 }
677 }, yt.speed, YAHOO.util.Easing.easeOut
677 }, yt.speed, YAHOO.util.Easing.easeOut
678 );
678 );
679 newAnim.animate();
679 newAnim.animate();
680 } else {
680 } else {
681 YUD.setStyle(yt.tipBox, 'display', 'none');
681 YUD.setStyle(yt.tipBox, 'display', 'none');
682 }
682 }
683 YUD.setAttribute(el,'title', YUD.getAttribute(el, 'tt_title'));
683 YUD.setAttribute(el,'title', YUD.getAttribute(el, 'tt_title'));
684 }
684 }
685 }
685 }
686
686
687 /**
687 /**
688 * Quick filter widget
688 * Quick filter widget
689 *
689 *
690 * @param target: filter input target
690 * @param target: filter input target
691 * @param nodes: list of nodes in html we want to filter.
691 * @param nodes: list of nodes in html we want to filter.
692 * @param display_element function that takes current node from nodes and
692 * @param display_element function that takes current node from nodes and
693 * does hide or show based on the node
693 * does hide or show based on the node
694 *
694 *
695 */
695 */
696 var q_filter = function(target,nodes,display_element){
696 var q_filter = function(target,nodes,display_element){
697
697
698 var nodes = nodes;
698 var nodes = nodes;
699 var q_filter_field = YUD.get(target);
699 var q_filter_field = YUD.get(target);
700 var F = YAHOO.namespace(target);
700 var F = YAHOO.namespace(target);
701
701
702 YUE.on(q_filter_field,'click',function(){
702 YUE.on(q_filter_field,'click',function(){
703 q_filter_field.value = '';
703 q_filter_field.value = '';
704 });
704 });
705
705
706 YUE.on(q_filter_field,'keyup',function(e){
706 YUE.on(q_filter_field,'keyup',function(e){
707 clearTimeout(F.filterTimeout);
707 clearTimeout(F.filterTimeout);
708 F.filterTimeout = setTimeout(F.updateFilter,600);
708 F.filterTimeout = setTimeout(F.updateFilter,600);
709 });
709 });
710
710
711 F.filterTimeout = null;
711 F.filterTimeout = null;
712
712
713 var show_node = function(node){
713 var show_node = function(node){
714 YUD.setStyle(node,'display','')
714 YUD.setStyle(node,'display','')
715 }
715 }
716 var hide_node = function(node){
716 var hide_node = function(node){
717 YUD.setStyle(node,'display','none');
717 YUD.setStyle(node,'display','none');
718 }
718 }
719
719
720 F.updateFilter = function() {
720 F.updateFilter = function() {
721 // Reset timeout
721 // Reset timeout
722 F.filterTimeout = null;
722 F.filterTimeout = null;
723
723
724 var obsolete = [];
724 var obsolete = [];
725
725
726 var req = q_filter_field.value.toLowerCase();
726 var req = q_filter_field.value.toLowerCase();
727
727
728 var l = nodes.length;
728 var l = nodes.length;
729 var i;
729 var i;
730 var showing = 0;
730 var showing = 0;
731
731
732 for (i=0;i<l;i++ ){
732 for (i=0;i<l;i++ ){
733 var n = nodes[i];
733 var n = nodes[i];
734 var target_element = display_element(n)
734 var target_element = display_element(n)
735 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
735 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
736 hide_node(target_element);
736 hide_node(target_element);
737 }
737 }
738 else{
738 else{
739 show_node(target_element);
739 show_node(target_element);
740 showing+=1;
740 showing+=1;
741 }
741 }
742 }
742 }
743
743
744 // if repo_count is set update the number
744 // if repo_count is set update the number
745 var cnt = YUD.get('repo_count');
745 var cnt = YUD.get('repo_count');
746 if(cnt){
746 if(cnt){
747 YUD.get('repo_count').innerHTML = showing;
747 YUD.get('repo_count').innerHTML = showing;
748 }
748 }
749
749
750 }
750 }
751 };
751 };
752
752
753 var tableTr = function(cls, body){
753 var tableTr = function(cls, body){
754 var _el = document.createElement('div');
754 var _el = document.createElement('div');
755 var cont = new YAHOO.util.Element(body);
755 var cont = new YAHOO.util.Element(body);
756 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
756 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
757 var id = 'comment-tr-{0}'.format(comment_id);
757 var id = 'comment-tr-{0}'.format(comment_id);
758 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
758 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
759 '<td class="lineno-inline new-inline"></td>'+
759 '<td class="lineno-inline new-inline"></td>'+
760 '<td class="lineno-inline old-inline"></td>'+
760 '<td class="lineno-inline old-inline"></td>'+
761 '<td>{2}</td>'+
761 '<td>{2}</td>'+
762 '</tr></tbody></table>').format(id, cls, body);
762 '</tr></tbody></table>').format(id, cls, body);
763 _el.innerHTML = _html;
763 _el.innerHTML = _html;
764 return _el.children[0].children[0].children[0];
764 return _el.children[0].children[0].children[0];
765 };
765 };
766
766
767 /** comments **/
767 /** comments **/
768 var removeInlineForm = function(form) {
768 var removeInlineForm = function(form) {
769 form.parentNode.removeChild(form);
769 form.parentNode.removeChild(form);
770 };
770 };
771
771
772 var createInlineForm = function(parent_tr, f_path, line) {
772 var createInlineForm = function(parent_tr, f_path, line) {
773 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
773 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
774 tmpl = tmpl.format(f_path, line);
774 tmpl = tmpl.format(f_path, line);
775 var form = tableTr('comment-form-inline',tmpl)
775 var form = tableTr('comment-form-inline',tmpl)
776
776
777 // create event for hide button
777 // create event for hide button
778 form = new YAHOO.util.Element(form);
778 form = new YAHOO.util.Element(form);
779 var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
779 var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
780 form_hide_button.on('click', function(e) {
780 form_hide_button.on('click', function(e) {
781 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
781 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
782 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
782 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
783 YUD.setStyle(newtr.nextElementSibling,'display','');
783 YUD.setStyle(newtr.nextElementSibling,'display','');
784 }
784 }
785 removeInlineForm(newtr);
785 removeInlineForm(newtr);
786 YUD.removeClass(parent_tr, 'form-open');
786 YUD.removeClass(parent_tr, 'form-open');
787 YUD.removeClass(parent_tr, 'hl-comment');
787 YUD.removeClass(parent_tr, 'hl-comment');
788
788
789 });
789 });
790
790
791 return form
791 return form
792 };
792 };
793
793
794 /**
794 /**
795 * Inject inline comment for on given TR this tr should be always an .line
795 * Inject inline comment for on given TR this tr should be always an .line
796 * tr containing the line. Code will detect comment, and always put the comment
796 * tr containing the line. Code will detect comment, and always put the comment
797 * block at the very bottom
797 * block at the very bottom
798 */
798 */
799 var injectInlineForm = function(tr){
799 var injectInlineForm = function(tr){
800 if(!YUD.hasClass(tr, 'line')){
800 if(!YUD.hasClass(tr, 'line')){
801 return
801 return
802 }
802 }
803 var submit_url = AJAX_COMMENT_URL;
803 var submit_url = AJAX_COMMENT_URL;
804 var _td = YUD.getElementsByClassName('code',null,tr)[0];
804 var _td = YUD.getElementsByClassName('code',null,tr)[0];
805 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
805 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
806 return
806 return
807 }
807 }
808 YUD.addClass(tr,'form-open');
808 YUD.addClass(tr,'form-open');
809 YUD.addClass(tr,'hl-comment');
809 YUD.addClass(tr,'hl-comment');
810 var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
810 var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
811 var f_path = YUD.getAttribute(node,'path');
811 var f_path = YUD.getAttribute(node,'path');
812 var lineno = getLineNo(tr);
812 var lineno = getLineNo(tr);
813 var form = createInlineForm(tr, f_path, lineno, submit_url);
813 var form = createInlineForm(tr, f_path, lineno, submit_url);
814
814
815 var parent = tr;
815 var parent = tr;
816 while (1){
816 while (1){
817 var n = parent.nextElementSibling;
817 var n = parent.nextElementSibling;
818 // next element are comments !
818 // next element are comments !
819 if(YUD.hasClass(n,'inline-comments')){
819 if(YUD.hasClass(n,'inline-comments')){
820 parent = n;
820 parent = n;
821 }
821 }
822 else{
822 else{
823 break;
823 break;
824 }
824 }
825 }
825 }
826 YUD.insertAfter(form,parent);
826 YUD.insertAfter(form,parent);
827 var f = YUD.get(form);
827 var f = YUD.get(form);
828 var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
828 var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
829 var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
829 var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
830
830
831 YUE.on(YUD.get(_form), 'submit',function(e){
831 YUE.on(YUD.get(_form), 'submit',function(e){
832 YUE.preventDefault(e);
832 YUE.preventDefault(e);
833
833
834 //ajax submit
834 //ajax submit
835 var text = YUD.get('text_'+lineno).value;
835 var text = YUD.get('text_'+lineno).value;
836 var postData = {
836 var postData = {
837 'text':text,
837 'text':text,
838 'f_path':f_path,
838 'f_path':f_path,
839 'line':lineno
839 'line':lineno
840 };
840 };
841
841
842 if(lineno === undefined){
842 if(lineno === undefined){
843 alert('missing line !');
843 alert('missing line !');
844 return
844 return
845 }
845 }
846 if(f_path === undefined){
846 if(f_path === undefined){
847 alert('missing file path !');
847 alert('missing file path !');
848 return
848 return
849 }
849 }
850
850
851 if(text == ""){
851 if(text == ""){
852 return
852 return
853 }
853 }
854
854
855 var success = function(o){
855 var success = function(o){
856 YUD.removeClass(tr, 'form-open');
856 YUD.removeClass(tr, 'form-open');
857 removeInlineForm(f);
857 removeInlineForm(f);
858 var json_data = JSON.parse(o.responseText);
858 var json_data = JSON.parse(o.responseText);
859 renderInlineComment(json_data);
859 renderInlineComment(json_data);
860 };
860 };
861
861
862 if (YUD.hasClass(overlay,'overlay')){
862 if (YUD.hasClass(overlay,'overlay')){
863 var w = _form.offsetWidth;
863 var w = _form.offsetWidth;
864 var h = _form.offsetHeight;
864 var h = _form.offsetHeight;
865 YUD.setStyle(overlay,'width',w+'px');
865 YUD.setStyle(overlay,'width',w+'px');
866 YUD.setStyle(overlay,'height',h+'px');
866 YUD.setStyle(overlay,'height',h+'px');
867 }
867 }
868 YUD.addClass(overlay, 'submitting');
868 YUD.addClass(overlay, 'submitting');
869
869
870 ajaxPOST(submit_url, postData, success);
870 ajaxPOST(submit_url, postData, success);
871 });
871 });
872
872
873 setTimeout(function(){
873 setTimeout(function(){
874 // callbacks
874 // callbacks
875 tooltip_activate();
875 tooltip_activate();
876 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
876 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
877 _USERS_AC_DATA, _GROUPS_AC_DATA);
877 _USERS_AC_DATA, _GROUPS_AC_DATA);
878 var _e = YUD.get('text_'+lineno);
878 var _e = YUD.get('text_'+lineno);
879 if(_e){
879 if(_e){
880 _e.focus();
880 _e.focus();
881 }
881 }
882 },10)
882 },10)
883 };
883 };
884
884
885 var deleteComment = function(comment_id){
885 var deleteComment = function(comment_id){
886 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
886 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
887 var postData = {'_method':'delete'};
887 var postData = {'_method':'delete'};
888 var success = function(o){
888 var success = function(o){
889 var n = YUD.get('comment-tr-'+comment_id);
889 var n = YUD.get('comment-tr-'+comment_id);
890 var root = prevElementSibling(prevElementSibling(n));
890 var root = prevElementSibling(prevElementSibling(n));
891 n.parentNode.removeChild(n);
891 n.parentNode.removeChild(n);
892
892
893 // scann nodes, and attach add button to last one
893 // scann nodes, and attach add button to last one
894 placeAddButton(root);
894 placeAddButton(root);
895 }
895 }
896 ajaxPOST(url,postData,success);
896 ajaxPOST(url,postData,success);
897 }
897 }
898
898
899 var createInlineAddButton = function(tr){
899 var createInlineAddButton = function(tr){
900
900
901 var label = TRANSLATION_MAP['Add another comment'];
901 var label = TRANSLATION_MAP['Add another comment'];
902
902
903 var html_el = document.createElement('div');
903 var html_el = document.createElement('div');
904 YUD.addClass(html_el, 'add-comment');
904 YUD.addClass(html_el, 'add-comment');
905 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
905 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
906
906
907 var add = new YAHOO.util.Element(html_el);
907 var add = new YAHOO.util.Element(html_el);
908 add.on('click', function(e) {
908 add.on('click', function(e) {
909 injectInlineForm(tr);
909 injectInlineForm(tr);
910 });
910 });
911 return add;
911 return add;
912 };
912 };
913
913
914 var getLineNo = function(tr) {
914 var getLineNo = function(tr) {
915 var line;
915 var line;
916 var o = tr.children[0].id.split('_');
916 var o = tr.children[0].id.split('_');
917 var n = tr.children[1].id.split('_');
917 var n = tr.children[1].id.split('_');
918
918
919 if (n.length >= 2) {
919 if (n.length >= 2) {
920 line = n[n.length-1];
920 line = n[n.length-1];
921 } else if (o.length >= 2) {
921 } else if (o.length >= 2) {
922 line = o[o.length-1];
922 line = o[o.length-1];
923 }
923 }
924
924
925 return line
925 return line
926 };
926 };
927
927
928 var placeAddButton = function(target_tr){
928 var placeAddButton = function(target_tr){
929 if(!target_tr){
929 if(!target_tr){
930 return
930 return
931 }
931 }
932 var last_node = target_tr;
932 var last_node = target_tr;
933 //scann
933 //scann
934 while (1){
934 while (1){
935 var n = last_node.nextElementSibling;
935 var n = last_node.nextElementSibling;
936 // next element are comments !
936 // next element are comments !
937 if(YUD.hasClass(n,'inline-comments')){
937 if(YUD.hasClass(n,'inline-comments')){
938 last_node = n;
938 last_node = n;
939 //also remove the comment button from previous
939 //also remove the comment button from previous
940 var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
940 var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
941 for(var i=0;i<comment_add_buttons.length;i++){
941 for(var i=0;i<comment_add_buttons.length;i++){
942 var b = comment_add_buttons[i];
942 var b = comment_add_buttons[i];
943 b.parentNode.removeChild(b);
943 b.parentNode.removeChild(b);
944 }
944 }
945 }
945 }
946 else{
946 else{
947 break;
947 break;
948 }
948 }
949 }
949 }
950
950
951 var add = createInlineAddButton(target_tr);
951 var add = createInlineAddButton(target_tr);
952 // get the comment div
952 // get the comment div
953 var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
953 var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
954 // attach add button
954 // attach add button
955 YUD.insertAfter(add,comment_block);
955 YUD.insertAfter(add,comment_block);
956 }
956 }
957
957
958 /**
958 /**
959 * Places the inline comment into the changeset block in proper line position
959 * Places the inline comment into the changeset block in proper line position
960 */
960 */
961 var placeInline = function(target_container,lineno,html){
961 var placeInline = function(target_container,lineno,html){
962 var lineid = "{0}_{1}".format(target_container,lineno);
962 var lineid = "{0}_{1}".format(target_container,lineno);
963 var target_line = YUD.get(lineid);
963 var target_line = YUD.get(lineid);
964 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
964 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
965
965
966 // check if there are comments already !
966 // check if there are comments already !
967 var parent = target_line.parentNode;
967 var parent = target_line.parentNode;
968 var root_parent = parent;
968 var root_parent = parent;
969 while (1){
969 while (1){
970 var n = parent.nextElementSibling;
970 var n = parent.nextElementSibling;
971 // next element are comments !
971 // next element are comments !
972 if(YUD.hasClass(n,'inline-comments')){
972 if(YUD.hasClass(n,'inline-comments')){
973 parent = n;
973 parent = n;
974 }
974 }
975 else{
975 else{
976 break;
976 break;
977 }
977 }
978 }
978 }
979 // put in the comment at the bottom
979 // put in the comment at the bottom
980 YUD.insertAfter(comment,parent);
980 YUD.insertAfter(comment,parent);
981
981
982 // scann nodes, and attach add button to last one
982 // scann nodes, and attach add button to last one
983 placeAddButton(root_parent);
983 placeAddButton(root_parent);
984
984
985 return target_line;
985 return target_line;
986 }
986 }
987
987
988 /**
988 /**
989 * make a single inline comment and place it inside
989 * make a single inline comment and place it inside
990 */
990 */
991 var renderInlineComment = function(json_data){
991 var renderInlineComment = function(json_data){
992 try{
992 try{
993 var html = json_data['rendered_text'];
993 var html = json_data['rendered_text'];
994 var lineno = json_data['line_no'];
994 var lineno = json_data['line_no'];
995 var target_id = json_data['target_id'];
995 var target_id = json_data['target_id'];
996 placeInline(target_id, lineno, html);
996 placeInline(target_id, lineno, html);
997
997
998 }catch(e){
998 }catch(e){
999 console.log(e);
999 console.log(e);
1000 }
1000 }
1001 }
1001 }
1002
1002
1003 /**
1003 /**
1004 * Iterates over all the inlines, and places them inside proper blocks of data
1004 * Iterates over all the inlines, and places them inside proper blocks of data
1005 */
1005 */
1006 var renderInlineComments = function(file_comments){
1006 var renderInlineComments = function(file_comments){
1007 for (f in file_comments){
1007 for (f in file_comments){
1008 // holding all comments for a FILE
1008 // holding all comments for a FILE
1009 var box = file_comments[f];
1009 var box = file_comments[f];
1010
1010
1011 var target_id = YUD.getAttribute(box,'target_id');
1011 var target_id = YUD.getAttribute(box,'target_id');
1012 // actually comments with line numbers
1012 // actually comments with line numbers
1013 var comments = box.children;
1013 var comments = box.children;
1014 for(var i=0; i<comments.length; i++){
1014 for(var i=0; i<comments.length; i++){
1015 var data = {
1015 var data = {
1016 'rendered_text': comments[i].outerHTML,
1016 'rendered_text': comments[i].outerHTML,
1017 'line_no': YUD.getAttribute(comments[i],'line'),
1017 'line_no': YUD.getAttribute(comments[i],'line'),
1018 'target_id': target_id
1018 'target_id': target_id
1019 }
1019 }
1020 renderInlineComment(data);
1020 renderInlineComment(data);
1021 }
1021 }
1022 }
1022 }
1023 }
1023 }
1024
1024
1025 var fileBrowserListeners = function(current_url, node_list_url, url_base){
1025 var fileBrowserListeners = function(current_url, node_list_url, url_base){
1026 var current_url_branch = +"?branch=__BRANCH__";
1026 var current_url_branch = +"?branch=__BRANCH__";
1027
1027
1028 YUE.on('stay_at_branch','click',function(e){
1028 YUE.on('stay_at_branch','click',function(e){
1029 if(e.target.checked){
1029 if(e.target.checked){
1030 var uri = current_url_branch;
1030 var uri = current_url_branch;
1031 uri = uri.replace('__BRANCH__',e.target.value);
1031 uri = uri.replace('__BRANCH__',e.target.value);
1032 window.location = uri;
1032 window.location = uri;
1033 }
1033 }
1034 else{
1034 else{
1035 window.location = current_url;
1035 window.location = current_url;
1036 }
1036 }
1037 })
1037 })
1038
1038
1039 var n_filter = YUD.get('node_filter');
1039 var n_filter = YUD.get('node_filter');
1040 var F = YAHOO.namespace('node_filter');
1040 var F = YAHOO.namespace('node_filter');
1041
1041
1042 F.filterTimeout = null;
1042 F.filterTimeout = null;
1043 var nodes = null;
1043 var nodes = null;
1044
1044
1045 F.initFilter = function(){
1045 F.initFilter = function(){
1046 YUD.setStyle('node_filter_box_loading','display','');
1046 YUD.setStyle('node_filter_box_loading','display','');
1047 YUD.setStyle('search_activate_id','display','none');
1047 YUD.setStyle('search_activate_id','display','none');
1048 YUD.setStyle('add_node_id','display','none');
1048 YUD.setStyle('add_node_id','display','none');
1049 YUC.initHeader('X-PARTIAL-XHR',true);
1049 YUC.initHeader('X-PARTIAL-XHR',true);
1050 YUC.asyncRequest('GET', node_list_url, {
1050 YUC.asyncRequest('GET', node_list_url, {
1051 success:function(o){
1051 success:function(o){
1052 nodes = JSON.parse(o.responseText).nodes;
1052 nodes = JSON.parse(o.responseText).nodes;
1053 YUD.setStyle('node_filter_box_loading','display','none');
1053 YUD.setStyle('node_filter_box_loading','display','none');
1054 YUD.setStyle('node_filter_box','display','');
1054 YUD.setStyle('node_filter_box','display','');
1055 n_filter.focus();
1055 n_filter.focus();
1056 if(YUD.hasClass(n_filter,'init')){
1056 if(YUD.hasClass(n_filter,'init')){
1057 n_filter.value = '';
1057 n_filter.value = '';
1058 YUD.removeClass(n_filter,'init');
1058 YUD.removeClass(n_filter,'init');
1059 }
1059 }
1060 },
1060 },
1061 failure:function(o){
1061 failure:function(o){
1062 console.log('failed to load');
1062 console.log('failed to load');
1063 }
1063 }
1064 },null);
1064 },null);
1065 }
1065 }
1066
1066
1067 F.updateFilter = function(e) {
1067 F.updateFilter = function(e) {
1068
1068
1069 return function(){
1069 return function(){
1070 // Reset timeout
1070 // Reset timeout
1071 F.filterTimeout = null;
1071 F.filterTimeout = null;
1072 var query = e.target.value.toLowerCase();
1072 var query = e.target.value.toLowerCase();
1073 var match = [];
1073 var match = [];
1074 var matches = 0;
1074 var matches = 0;
1075 var matches_max = 20;
1075 var matches_max = 20;
1076 if (query != ""){
1076 if (query != ""){
1077 for(var i=0;i<nodes.length;i++){
1077 for(var i=0;i<nodes.length;i++){
1078
1078
1079 var pos = nodes[i].name.toLowerCase().indexOf(query)
1079 var pos = nodes[i].name.toLowerCase().indexOf(query)
1080 if(query && pos != -1){
1080 if(query && pos != -1){
1081
1081
1082 matches++
1082 matches++
1083 //show only certain amount to not kill browser
1083 //show only certain amount to not kill browser
1084 if (matches > matches_max){
1084 if (matches > matches_max){
1085 break;
1085 break;
1086 }
1086 }
1087
1087
1088 var n = nodes[i].name;
1088 var n = nodes[i].name;
1089 var t = nodes[i].type;
1089 var t = nodes[i].type;
1090 var n_hl = n.substring(0,pos)
1090 var n_hl = n.substring(0,pos)
1091 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
1091 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
1092 +n.substring(pos+query.length)
1092 +n.substring(pos+query.length)
1093 var new_url = url_base.replace('__FPATH__',n);
1093 var new_url = url_base.replace('__FPATH__',n);
1094 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
1094 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
1095 }
1095 }
1096 if(match.length >= matches_max){
1096 if(match.length >= matches_max){
1097 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
1097 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
1098 }
1098 }
1099 }
1099 }
1100 }
1100 }
1101 if(query != ""){
1101 if(query != ""){
1102 YUD.setStyle('tbody','display','none');
1102 YUD.setStyle('tbody','display','none');
1103 YUD.setStyle('tbody_filtered','display','');
1103 YUD.setStyle('tbody_filtered','display','');
1104
1104
1105 if (match.length==0){
1105 if (match.length==0){
1106 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
1106 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
1107 }
1107 }
1108
1108
1109 YUD.get('tbody_filtered').innerHTML = match.join("");
1109 YUD.get('tbody_filtered').innerHTML = match.join("");
1110 }
1110 }
1111 else{
1111 else{
1112 YUD.setStyle('tbody','display','');
1112 YUD.setStyle('tbody','display','');
1113 YUD.setStyle('tbody_filtered','display','none');
1113 YUD.setStyle('tbody_filtered','display','none');
1114 }
1114 }
1115
1115
1116 }
1116 }
1117 };
1117 };
1118
1118
1119 YUE.on(YUD.get('filter_activate'),'click',function(){
1119 YUE.on(YUD.get('filter_activate'),'click',function(){
1120 F.initFilter();
1120 F.initFilter();
1121 })
1121 })
1122 YUE.on(n_filter,'click',function(){
1122 YUE.on(n_filter,'click',function(){
1123 if(YUD.hasClass(n_filter,'init')){
1123 if(YUD.hasClass(n_filter,'init')){
1124 n_filter.value = '';
1124 n_filter.value = '';
1125 YUD.removeClass(n_filter,'init');
1125 YUD.removeClass(n_filter,'init');
1126 }
1126 }
1127 });
1127 });
1128 YUE.on(n_filter,'keyup',function(e){
1128 YUE.on(n_filter,'keyup',function(e){
1129 clearTimeout(F.filterTimeout);
1129 clearTimeout(F.filterTimeout);
1130 F.filterTimeout = setTimeout(F.updateFilter(e),600);
1130 F.filterTimeout = setTimeout(F.updateFilter(e),600);
1131 });
1131 });
1132 };
1132 };
1133
1133
1134
1134
1135 var initCodeMirror = function(textAreadId,resetUrl){
1135 var initCodeMirror = function(textAreadId,resetUrl){
1136 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
1136 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
1137 mode: "null",
1137 mode: "null",
1138 lineNumbers:true
1138 lineNumbers:true
1139 });
1139 });
1140 YUE.on('reset','click',function(e){
1140 YUE.on('reset','click',function(e){
1141 window.location=resetUrl
1141 window.location=resetUrl
1142 });
1142 });
1143
1143
1144 YUE.on('file_enable','click',function(){
1144 YUE.on('file_enable','click',function(){
1145 YUD.setStyle('editor_container','display','');
1145 YUD.setStyle('editor_container','display','');
1146 YUD.setStyle('upload_file_container','display','none');
1146 YUD.setStyle('upload_file_container','display','none');
1147 YUD.setStyle('filename_container','display','');
1147 YUD.setStyle('filename_container','display','');
1148 });
1148 });
1149
1149
1150 YUE.on('upload_file_enable','click',function(){
1150 YUE.on('upload_file_enable','click',function(){
1151 YUD.setStyle('editor_container','display','none');
1151 YUD.setStyle('editor_container','display','none');
1152 YUD.setStyle('upload_file_container','display','');
1152 YUD.setStyle('upload_file_container','display','');
1153 YUD.setStyle('filename_container','display','none');
1153 YUD.setStyle('filename_container','display','none');
1154 });
1154 });
1155 };
1155 };
1156
1156
1157
1157
1158
1158
1159 var getIdentNode = function(n){
1159 var getIdentNode = function(n){
1160 //iterate thru nodes untill matched interesting node !
1160 //iterate thru nodes untill matched interesting node !
1161
1161
1162 if (typeof n == 'undefined'){
1162 if (typeof n == 'undefined'){
1163 return -1
1163 return -1
1164 }
1164 }
1165
1165
1166 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1166 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1167 return n
1167 return n
1168 }
1168 }
1169 else{
1169 else{
1170 return getIdentNode(n.parentNode);
1170 return getIdentNode(n.parentNode);
1171 }
1171 }
1172 };
1172 };
1173
1173
1174 var getSelectionLink = function(e) {
1174 var getSelectionLink = function(e) {
1175
1175
1176 //get selection from start/to nodes
1176 //get selection from start/to nodes
1177 if (typeof window.getSelection != "undefined") {
1177 if (typeof window.getSelection != "undefined") {
1178 s = window.getSelection();
1178 s = window.getSelection();
1179
1179
1180 from = getIdentNode(s.anchorNode);
1180 from = getIdentNode(s.anchorNode);
1181 till = getIdentNode(s.focusNode);
1181 till = getIdentNode(s.focusNode);
1182
1182
1183 f_int = parseInt(from.id.replace('L',''));
1183 f_int = parseInt(from.id.replace('L',''));
1184 t_int = parseInt(till.id.replace('L',''));
1184 t_int = parseInt(till.id.replace('L',''));
1185
1185
1186 if (f_int > t_int){
1186 if (f_int > t_int){
1187 //highlight from bottom
1187 //highlight from bottom
1188 offset = -35;
1188 offset = -35;
1189 ranges = [t_int,f_int];
1189 ranges = [t_int,f_int];
1190
1190
1191 }
1191 }
1192 else{
1192 else{
1193 //highligth from top
1193 //highligth from top
1194 offset = 35;
1194 offset = 35;
1195 ranges = [f_int,t_int];
1195 ranges = [f_int,t_int];
1196 }
1196 }
1197 // if we select more than 2 lines
1197 // if we select more than 2 lines
1198 if (ranges[0] != ranges[1]){
1198 if (ranges[0] != ranges[1]){
1199 if(YUD.get('linktt') == null){
1199 if(YUD.get('linktt') == null){
1200 hl_div = document.createElement('div');
1200 hl_div = document.createElement('div');
1201 hl_div.id = 'linktt';
1201 hl_div.id = 'linktt';
1202 }
1202 }
1203 hl_div.innerHTML = '';
1203 hl_div.innerHTML = '';
1204
1204
1205 anchor = '#L'+ranges[0]+'-'+ranges[1];
1205 anchor = '#L'+ranges[0]+'-'+ranges[1];
1206 var link = document.createElement('a');
1206 var link = document.createElement('a');
1207 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
1207 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
1208 link.innerHTML = _TM['Selection link'];
1208 link.innerHTML = _TM['Selection link'];
1209 hl_div.appendChild(link);
1209 hl_div.appendChild(link);
1210 YUD.get('body').appendChild(hl_div);
1210 YUD.get('body').appendChild(hl_div);
1211
1211
1212 xy = YUD.getXY(till.id);
1212 xy = YUD.getXY(till.id);
1213
1213
1214 YUD.addClass('linktt', 'hl-tip-box');
1214 YUD.addClass('linktt', 'hl-tip-box');
1215 YUD.setStyle('linktt','top',xy[1]+offset+'px');
1215 YUD.setStyle('linktt','top',xy[1]+offset+'px');
1216 YUD.setStyle('linktt','left',xy[0]+'px');
1216 YUD.setStyle('linktt','left',xy[0]+'px');
1217 YUD.setStyle('linktt','visibility','visible');
1217 YUD.setStyle('linktt','visibility','visible');
1218
1218
1219 }
1219 }
1220 else{
1220 else{
1221 YUD.setStyle('linktt','visibility','hidden');
1221 YUD.setStyle('linktt','visibility','hidden');
1222 }
1222 }
1223 }
1223 }
1224 };
1224 };
1225
1225
1226 var deleteNotification = function(url, notification_id,callbacks){
1226 var deleteNotification = function(url, notification_id,callbacks){
1227 var callback = {
1227 var callback = {
1228 success:function(o){
1228 success:function(o){
1229 var obj = YUD.get(String("notification_"+notification_id));
1229 var obj = YUD.get(String("notification_"+notification_id));
1230 if(obj.parentNode !== undefined){
1230 if(obj.parentNode !== undefined){
1231 obj.parentNode.removeChild(obj);
1231 obj.parentNode.removeChild(obj);
1232 }
1232 }
1233 _run_callbacks(callbacks);
1233 _run_callbacks(callbacks);
1234 },
1234 },
1235 failure:function(o){
1235 failure:function(o){
1236 alert("error");
1236 alert("error");
1237 },
1237 },
1238 };
1238 };
1239 var postData = '_method=delete';
1239 var postData = '_method=delete';
1240 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1240 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1241 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1241 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1242 callback, postData);
1242 callback, postData);
1243 };
1243 };
1244
1244
1245 var readNotification = function(url, notification_id,callbacks){
1245 var readNotification = function(url, notification_id,callbacks){
1246 var callback = {
1246 var callback = {
1247 success:function(o){
1247 success:function(o){
1248 var obj = YUD.get(String("notification_"+notification_id));
1248 var obj = YUD.get(String("notification_"+notification_id));
1249 YUD.removeClass(obj, 'unread');
1249 YUD.removeClass(obj, 'unread');
1250 var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
1250 var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
1251
1251
1252 if(r_button.parentNode !== undefined){
1252 if(r_button.parentNode !== undefined){
1253 r_button.parentNode.removeChild(r_button);
1253 r_button.parentNode.removeChild(r_button);
1254 }
1254 }
1255 _run_callbacks(callbacks);
1255 _run_callbacks(callbacks);
1256 },
1256 },
1257 failure:function(o){
1257 failure:function(o){
1258 alert("error");
1258 alert("error");
1259 },
1259 },
1260 };
1260 };
1261 var postData = '_method=put';
1261 var postData = '_method=put';
1262 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1262 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1263 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1263 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1264 callback, postData);
1264 callback, postData);
1265 };
1265 };
1266
1266
1267 /** MEMBERS AUTOCOMPLETE WIDGET **/
1267 /** MEMBERS AUTOCOMPLETE WIDGET **/
1268
1268
1269 var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1269 var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1270 var myUsers = users_list;
1270 var myUsers = users_list;
1271 var myGroups = groups_list;
1271 var myGroups = groups_list;
1272
1272
1273 // Define a custom search function for the DataSource of users
1273 // Define a custom search function for the DataSource of users
1274 var matchUsers = function (sQuery) {
1274 var matchUsers = function (sQuery) {
1275 // Case insensitive matching
1275 // Case insensitive matching
1276 var query = sQuery.toLowerCase();
1276 var query = sQuery.toLowerCase();
1277 var i = 0;
1277 var i = 0;
1278 var l = myUsers.length;
1278 var l = myUsers.length;
1279 var matches = [];
1279 var matches = [];
1280
1280
1281 // Match against each name of each contact
1281 // Match against each name of each contact
1282 for (; i < l; i++) {
1282 for (; i < l; i++) {
1283 contact = myUsers[i];
1283 contact = myUsers[i];
1284 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1284 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1285 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1285 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1286 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1286 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1287 matches[matches.length] = contact;
1287 matches[matches.length] = contact;
1288 }
1288 }
1289 }
1289 }
1290 return matches;
1290 return matches;
1291 };
1291 };
1292
1292
1293 // Define a custom search function for the DataSource of userGroups
1293 // Define a custom search function for the DataSource of userGroups
1294 var matchGroups = function (sQuery) {
1294 var matchGroups = function (sQuery) {
1295 // Case insensitive matching
1295 // Case insensitive matching
1296 var query = sQuery.toLowerCase();
1296 var query = sQuery.toLowerCase();
1297 var i = 0;
1297 var i = 0;
1298 var l = myGroups.length;
1298 var l = myGroups.length;
1299 var matches = [];
1299 var matches = [];
1300
1300
1301 // Match against each name of each contact
1301 // Match against each name of each contact
1302 for (; i < l; i++) {
1302 for (; i < l; i++) {
1303 matched_group = myGroups[i];
1303 matched_group = myGroups[i];
1304 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1304 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1305 matches[matches.length] = matched_group;
1305 matches[matches.length] = matched_group;
1306 }
1306 }
1307 }
1307 }
1308 return matches;
1308 return matches;
1309 };
1309 };
1310
1310
1311 //match all
1311 //match all
1312 var matchAll = function (sQuery) {
1312 var matchAll = function (sQuery) {
1313 u = matchUsers(sQuery);
1313 u = matchUsers(sQuery);
1314 g = matchGroups(sQuery);
1314 g = matchGroups(sQuery);
1315 return u.concat(g);
1315 return u.concat(g);
1316 };
1316 };
1317
1317
1318 // DataScheme for members
1318 // DataScheme for members
1319 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1319 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1320 memberDS.responseSchema = {
1320 memberDS.responseSchema = {
1321 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1321 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1322 };
1322 };
1323
1323
1324 // DataScheme for owner
1324 // DataScheme for owner
1325 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1325 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1326 ownerDS.responseSchema = {
1326 ownerDS.responseSchema = {
1327 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1327 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1328 };
1328 };
1329
1329
1330 // Instantiate AutoComplete for perms
1330 // Instantiate AutoComplete for perms
1331 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1331 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1332 membersAC.useShadow = false;
1332 membersAC.useShadow = false;
1333 membersAC.resultTypeList = false;
1333 membersAC.resultTypeList = false;
1334 membersAC.animVert = false;
1334 membersAC.animVert = false;
1335 membersAC.animHoriz = false;
1335 membersAC.animHoriz = false;
1336 membersAC.animSpeed = 0.1;
1336 membersAC.animSpeed = 0.1;
1337
1337
1338 // Instantiate AutoComplete for owner
1338 // Instantiate AutoComplete for owner
1339 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1339 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1340 ownerAC.useShadow = false;
1340 ownerAC.useShadow = false;
1341 ownerAC.resultTypeList = false;
1341 ownerAC.resultTypeList = false;
1342 ownerAC.animVert = false;
1342 ownerAC.animVert = false;
1343 ownerAC.animHoriz = false;
1343 ownerAC.animHoriz = false;
1344 ownerAC.animSpeed = 0.1;
1344 ownerAC.animSpeed = 0.1;
1345
1345
1346 // Helper highlight function for the formatter
1346 // Helper highlight function for the formatter
1347 var highlightMatch = function (full, snippet, matchindex) {
1347 var highlightMatch = function (full, snippet, matchindex) {
1348 return full.substring(0, matchindex)
1348 return full.substring(0, matchindex)
1349 + "<span class='match'>"
1349 + "<span class='match'>"
1350 + full.substr(matchindex, snippet.length)
1350 + full.substr(matchindex, snippet.length)
1351 + "</span>" + full.substring(matchindex + snippet.length);
1351 + "</span>" + full.substring(matchindex + snippet.length);
1352 };
1352 };
1353
1353
1354 // Custom formatter to highlight the matching letters
1354 // Custom formatter to highlight the matching letters
1355 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1355 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1356 var query = sQuery.toLowerCase();
1356 var query = sQuery.toLowerCase();
1357 var _gravatar = function(res, em, group){
1357 var _gravatar = function(res, em, group){
1358 if (group !== undefined){
1358 if (group !== undefined){
1359 em = '/images/icons/group.png'
1359 em = '/images/icons/group.png'
1360 }
1360 }
1361 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1361 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1362 return tmpl.format(em,res)
1362 return tmpl.format(em,res)
1363 }
1363 }
1364 // group
1364 // group
1365 if (oResultData.grname != undefined) {
1365 if (oResultData.grname != undefined) {
1366 var grname = oResultData.grname;
1366 var grname = oResultData.grname;
1367 var grmembers = oResultData.grmembers;
1367 var grmembers = oResultData.grmembers;
1368 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1368 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1369 var grprefix = "{0}: ".format(_TM['Group']);
1369 var grprefix = "{0}: ".format(_TM['Group']);
1370 var grsuffix = " (" + grmembers + " )";
1370 var grsuffix = " (" + grmembers + " )";
1371 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1371 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1372
1372
1373 if (grnameMatchIndex > -1) {
1373 if (grnameMatchIndex > -1) {
1374 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1374 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1375 }
1375 }
1376 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1376 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1377 // Users
1377 // Users
1378 } else if (oResultData.nname != undefined) {
1378 } else if (oResultData.nname != undefined) {
1379 var fname = oResultData.fname || "";
1379 var fname = oResultData.fname || "";
1380 var lname = oResultData.lname || "";
1380 var lname = oResultData.lname || "";
1381 var nname = oResultData.nname;
1381 var nname = oResultData.nname;
1382
1382
1383 // Guard against null value
1383 // Guard against null value
1384 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1384 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1385 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1385 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1386 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1386 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1387 displayfname, displaylname, displaynname;
1387 displayfname, displaylname, displaynname;
1388
1388
1389 if (fnameMatchIndex > -1) {
1389 if (fnameMatchIndex > -1) {
1390 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1390 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1391 } else {
1391 } else {
1392 displayfname = fname;
1392 displayfname = fname;
1393 }
1393 }
1394
1394
1395 if (lnameMatchIndex > -1) {
1395 if (lnameMatchIndex > -1) {
1396 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1396 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1397 } else {
1397 } else {
1398 displaylname = lname;
1398 displaylname = lname;
1399 }
1399 }
1400
1400
1401 if (nnameMatchIndex > -1) {
1401 if (nnameMatchIndex > -1) {
1402 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1402 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1403 } else {
1403 } else {
1404 displaynname = nname ? "(" + nname + ")" : "";
1404 displaynname = nname ? "(" + nname + ")" : "";
1405 }
1405 }
1406
1406
1407 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1407 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1408 } else {
1408 } else {
1409 return '';
1409 return '';
1410 }
1410 }
1411 };
1411 };
1412 membersAC.formatResult = custom_formatter;
1412 membersAC.formatResult = custom_formatter;
1413 ownerAC.formatResult = custom_formatter;
1413 ownerAC.formatResult = custom_formatter;
1414
1414
1415 var myHandler = function (sType, aArgs) {
1415 var myHandler = function (sType, aArgs) {
1416 var nextId = divid.split('perm_new_member_name_')[1];
1416 var nextId = divid.split('perm_new_member_name_')[1];
1417 var myAC = aArgs[0]; // reference back to the AC instance
1417 var myAC = aArgs[0]; // reference back to the AC instance
1418 var elLI = aArgs[1]; // reference to the selected LI element
1418 var elLI = aArgs[1]; // reference to the selected LI element
1419 var oData = aArgs[2]; // object literal of selected item's result data
1419 var oData = aArgs[2]; // object literal of selected item's result data
1420 //fill the autocomplete with value
1420 //fill the autocomplete with value
1421 if (oData.nname != undefined) {
1421 if (oData.nname != undefined) {
1422 //users
1422 //users
1423 myAC.getInputEl().value = oData.nname;
1423 myAC.getInputEl().value = oData.nname;
1424 YUD.get('perm_new_member_type_'+nextId).value = 'user';
1424 YUD.get('perm_new_member_type_'+nextId).value = 'user';
1425 } else {
1425 } else {
1426 //groups
1426 //groups
1427 myAC.getInputEl().value = oData.grname;
1427 myAC.getInputEl().value = oData.grname;
1428 YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
1428 YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
1429 }
1429 }
1430 };
1430 };
1431
1431
1432 membersAC.itemSelectEvent.subscribe(myHandler);
1432 membersAC.itemSelectEvent.subscribe(myHandler);
1433 if(ownerAC.itemSelectEvent){
1433 if(ownerAC.itemSelectEvent){
1434 ownerAC.itemSelectEvent.subscribe(myHandler);
1434 ownerAC.itemSelectEvent.subscribe(myHandler);
1435 }
1435 }
1436
1436
1437 return {
1437 return {
1438 memberDS: memberDS,
1438 memberDS: memberDS,
1439 ownerDS: ownerDS,
1439 ownerDS: ownerDS,
1440 membersAC: membersAC,
1440 membersAC: membersAC,
1441 ownerAC: ownerAC,
1441 ownerAC: ownerAC,
1442 };
1442 };
1443 }
1443 }
1444
1444
1445
1445
1446 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1446 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1447 var myUsers = users_list;
1447 var myUsers = users_list;
1448 var myGroups = groups_list;
1448 var myGroups = groups_list;
1449
1449
1450 // Define a custom search function for the DataSource of users
1450 // Define a custom search function for the DataSource of users
1451 var matchUsers = function (sQuery) {
1451 var matchUsers = function (sQuery) {
1452 var org_sQuery = sQuery;
1452 var org_sQuery = sQuery;
1453 if(this.mentionQuery == null){
1453 if(this.mentionQuery == null){
1454 return []
1454 return []
1455 }
1455 }
1456 sQuery = this.mentionQuery;
1456 sQuery = this.mentionQuery;
1457 // Case insensitive matching
1457 // Case insensitive matching
1458 var query = sQuery.toLowerCase();
1458 var query = sQuery.toLowerCase();
1459 var i = 0;
1459 var i = 0;
1460 var l = myUsers.length;
1460 var l = myUsers.length;
1461 var matches = [];
1461 var matches = [];
1462
1462
1463 // Match against each name of each contact
1463 // Match against each name of each contact
1464 for (; i < l; i++) {
1464 for (; i < l; i++) {
1465 contact = myUsers[i];
1465 contact = myUsers[i];
1466 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1466 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1467 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1467 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1468 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1468 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1469 matches[matches.length] = contact;
1469 matches[matches.length] = contact;
1470 }
1470 }
1471 }
1471 }
1472 return matches
1472 return matches
1473 };
1473 };
1474
1474
1475 //match all
1475 //match all
1476 var matchAll = function (sQuery) {
1476 var matchAll = function (sQuery) {
1477 u = matchUsers(sQuery);
1477 u = matchUsers(sQuery);
1478 return u
1478 return u
1479 };
1479 };
1480
1480
1481 // DataScheme for owner
1481 // DataScheme for owner
1482 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1482 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1483
1483
1484 ownerDS.responseSchema = {
1484 ownerDS.responseSchema = {
1485 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1485 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1486 };
1486 };
1487
1487
1488 // Instantiate AutoComplete for mentions
1488 // Instantiate AutoComplete for mentions
1489 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1489 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1490 ownerAC.useShadow = false;
1490 ownerAC.useShadow = false;
1491 ownerAC.resultTypeList = false;
1491 ownerAC.resultTypeList = false;
1492 ownerAC.suppressInputUpdate = true;
1492 ownerAC.suppressInputUpdate = true;
1493 ownerAC.animVert = false;
1493 ownerAC.animVert = false;
1494 ownerAC.animHoriz = false;
1494 ownerAC.animHoriz = false;
1495 ownerAC.animSpeed = 0.1;
1495 ownerAC.animSpeed = 0.1;
1496
1496
1497 // Helper highlight function for the formatter
1497 // Helper highlight function for the formatter
1498 var highlightMatch = function (full, snippet, matchindex) {
1498 var highlightMatch = function (full, snippet, matchindex) {
1499 return full.substring(0, matchindex)
1499 return full.substring(0, matchindex)
1500 + "<span class='match'>"
1500 + "<span class='match'>"
1501 + full.substr(matchindex, snippet.length)
1501 + full.substr(matchindex, snippet.length)
1502 + "</span>" + full.substring(matchindex + snippet.length);
1502 + "</span>" + full.substring(matchindex + snippet.length);
1503 };
1503 };
1504
1504
1505 // Custom formatter to highlight the matching letters
1505 // Custom formatter to highlight the matching letters
1506 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1506 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1507 var org_sQuery = sQuery;
1507 var org_sQuery = sQuery;
1508 if(this.dataSource.mentionQuery != null){
1508 if(this.dataSource.mentionQuery != null){
1509 sQuery = this.dataSource.mentionQuery;
1509 sQuery = this.dataSource.mentionQuery;
1510 }
1510 }
1511
1511
1512 var query = sQuery.toLowerCase();
1512 var query = sQuery.toLowerCase();
1513 var _gravatar = function(res, em, group){
1513 var _gravatar = function(res, em, group){
1514 if (group !== undefined){
1514 if (group !== undefined){
1515 em = '/images/icons/group.png'
1515 em = '/images/icons/group.png'
1516 }
1516 }
1517 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1517 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1518 return tmpl.format(em,res)
1518 return tmpl.format(em,res)
1519 }
1519 }
1520 if (oResultData.nname != undefined) {
1520 if (oResultData.nname != undefined) {
1521 var fname = oResultData.fname || "";
1521 var fname = oResultData.fname || "";
1522 var lname = oResultData.lname || "";
1522 var lname = oResultData.lname || "";
1523 var nname = oResultData.nname;
1523 var nname = oResultData.nname;
1524
1524
1525 // Guard against null value
1525 // Guard against null value
1526 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1526 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1527 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1527 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1528 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1528 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1529 displayfname, displaylname, displaynname;
1529 displayfname, displaylname, displaynname;
1530
1530
1531 if (fnameMatchIndex > -1) {
1531 if (fnameMatchIndex > -1) {
1532 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1532 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1533 } else {
1533 } else {
1534 displayfname = fname;
1534 displayfname = fname;
1535 }
1535 }
1536
1536
1537 if (lnameMatchIndex > -1) {
1537 if (lnameMatchIndex > -1) {
1538 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1538 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1539 } else {
1539 } else {
1540 displaylname = lname;
1540 displaylname = lname;
1541 }
1541 }
1542
1542
1543 if (nnameMatchIndex > -1) {
1543 if (nnameMatchIndex > -1) {
1544 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1544 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1545 } else {
1545 } else {
1546 displaynname = nname ? "(" + nname + ")" : "";
1546 displaynname = nname ? "(" + nname + ")" : "";
1547 }
1547 }
1548
1548
1549 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1549 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1550 } else {
1550 } else {
1551 return '';
1551 return '';
1552 }
1552 }
1553 };
1553 };
1554
1554
1555 if(ownerAC.itemSelectEvent){
1555 if(ownerAC.itemSelectEvent){
1556 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1556 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1557
1557
1558 var myAC = aArgs[0]; // reference back to the AC instance
1558 var myAC = aArgs[0]; // reference back to the AC instance
1559 var elLI = aArgs[1]; // reference to the selected LI element
1559 var elLI = aArgs[1]; // reference to the selected LI element
1560 var oData = aArgs[2]; // object literal of selected item's result data
1560 var oData = aArgs[2]; // object literal of selected item's result data
1561 //fill the autocomplete with value
1561 //fill the autocomplete with value
1562 if (oData.nname != undefined) {
1562 if (oData.nname != undefined) {
1563 //users
1563 //users
1564 //Replace the mention name with replaced
1564 //Replace the mention name with replaced
1565 var re = new RegExp();
1565 var re = new RegExp();
1566 var org = myAC.getInputEl().value;
1566 var org = myAC.getInputEl().value;
1567 var chunks = myAC.dataSource.chunks
1567 var chunks = myAC.dataSource.chunks
1568 // replace middle chunk(the search term) with actuall match
1568 // replace middle chunk(the search term) with actuall match
1569 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1569 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1570 '@'+oData.nname+' ');
1570 '@'+oData.nname+' ');
1571 myAC.getInputEl().value = chunks.join('')
1571 myAC.getInputEl().value = chunks.join('')
1572 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1572 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1573 } else {
1573 } else {
1574 //groups
1574 //groups
1575 myAC.getInputEl().value = oData.grname;
1575 myAC.getInputEl().value = oData.grname;
1576 YUD.get('perm_new_member_type').value = 'users_group';
1576 YUD.get('perm_new_member_type').value = 'users_group';
1577 }
1577 }
1578 });
1578 });
1579 }
1579 }
1580
1580
1581 // in this keybuffer we will gather current value of search !
1581 // in this keybuffer we will gather current value of search !
1582 // since we need to get this just when someone does `@` then we do the
1582 // since we need to get this just when someone does `@` then we do the
1583 // search
1583 // search
1584 ownerAC.dataSource.chunks = [];
1584 ownerAC.dataSource.chunks = [];
1585 ownerAC.dataSource.mentionQuery = null;
1585 ownerAC.dataSource.mentionQuery = null;
1586
1586
1587 ownerAC.get_mention = function(msg, max_pos) {
1587 ownerAC.get_mention = function(msg, max_pos) {
1588 var org = msg;
1588 var org = msg;
1589 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1589 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1590 var chunks = [];
1590 var chunks = [];
1591
1591
1592
1592
1593 // cut first chunk until curret pos
1593 // cut first chunk until curret pos
1594 var to_max = msg.substr(0, max_pos);
1594 var to_max = msg.substr(0, max_pos);
1595 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1595 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1596 var msg2 = to_max.substr(at_pos);
1596 var msg2 = to_max.substr(at_pos);
1597
1597
1598 chunks.push(org.substr(0,at_pos))// prefix chunk
1598 chunks.push(org.substr(0,at_pos))// prefix chunk
1599 chunks.push(msg2) // search chunk
1599 chunks.push(msg2) // search chunk
1600 chunks.push(org.substr(max_pos)) // postfix chunk
1600 chunks.push(org.substr(max_pos)) // postfix chunk
1601
1601
1602 // clean up msg2 for filtering and regex match
1602 // clean up msg2 for filtering and regex match
1603 var msg2 = msg2.lstrip(' ').lstrip('\n');
1603 var msg2 = msg2.lstrip(' ').lstrip('\n');
1604
1604
1605 if(re.test(msg2)){
1605 if(re.test(msg2)){
1606 var unam = re.exec(msg2)[1];
1606 var unam = re.exec(msg2)[1];
1607 return [unam, chunks];
1607 return [unam, chunks];
1608 }
1608 }
1609 return [null, null];
1609 return [null, null];
1610 };
1610 };
1611
1611
1612 if (ownerAC.textboxKeyUpEvent){
1612 if (ownerAC.textboxKeyUpEvent){
1613 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1613 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1614
1614
1615 var ac_obj = args[0];
1615 var ac_obj = args[0];
1616 var currentMessage = args[1];
1616 var currentMessage = args[1];
1617 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1617 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1618
1618
1619 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1619 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1620 var curr_search = null;
1620 var curr_search = null;
1621 if(unam[0]){
1621 if(unam[0]){
1622 curr_search = unam[0];
1622 curr_search = unam[0];
1623 }
1623 }
1624
1624
1625 ownerAC.dataSource.chunks = unam[1];
1625 ownerAC.dataSource.chunks = unam[1];
1626 ownerAC.dataSource.mentionQuery = curr_search;
1626 ownerAC.dataSource.mentionQuery = curr_search;
1627
1627
1628 })
1628 })
1629 }
1629 }
1630 return {
1630 return {
1631 ownerDS: ownerDS,
1631 ownerDS: ownerDS,
1632 ownerAC: ownerAC,
1632 ownerAC: ownerAC,
1633 };
1633 };
1634 }
1634 }
1635
1635
1636 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1636 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1637 var members = YUD.get('review_members');
1637 var members = YUD.get('review_members');
1638 var tmpl = '<li id="reviewer_{2}">'+
1638 var tmpl = '<li id="reviewer_{2}">'+
1639 '<div class="reviewers_member">'+
1639 '<div class="reviewers_member">'+
1640 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1640 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1641 '<div style="float:left">{1}</div>'+
1641 '<div style="float:left">{1}</div>'+
1642 '<input type="hidden" value="{2}" name="review_members" />'+
1642 '<input type="hidden" value="{2}" name="review_members" />'+
1643 '<span class="delete_icon action_button" onclick="removeReviewMember({2})"></span>'+
1643 '<span class="delete_icon action_button" onclick="removeReviewMember({2})"></span>'+
1644 '</div>'+
1644 '</div>'+
1645 '</li>' ;
1645 '</li>' ;
1646 var displayname = "{0} {1} ({2})".format(fname,lname,nname);
1646 var displayname = "{0} {1} ({2})".format(fname,lname,nname);
1647 var element = tmpl.format(gravatar_link,displayname,id);
1647 var element = tmpl.format(gravatar_link,displayname,id);
1648 // check if we don't have this ID already in
1648 // check if we don't have this ID already in
1649 var ids = [];
1649 var ids = [];
1650 var _els = YUQ('#review_members li');
1650 var _els = YUQ('#review_members li');
1651 for (el in _els){
1651 for (el in _els){
1652 ids.push(_els[el].id)
1652 ids.push(_els[el].id)
1653 }
1653 }
1654 if(ids.indexOf('reviewer_'+id) == -1){
1654 if(ids.indexOf('reviewer_'+id) == -1){
1655 //only add if it's not there
1655 //only add if it's not there
1656 members.innerHTML += element;
1656 members.innerHTML += element;
1657 }
1657 }
1658
1658
1659 }
1659 }
1660
1660
1661 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1661 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1662 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
1662 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
1663 if (el.parentNode !== undefined){
1663 if (el.parentNode !== undefined){
1664 el.parentNode.removeChild(el);
1664 el.parentNode.removeChild(el);
1665 }
1665 }
1666 }
1666 }
1667
1667
1668 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1668 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1669 if (reviewers_ids === undefined){
1669 if (reviewers_ids === undefined){
1670 var reviewers_ids = [];
1670 var reviewers_ids = [];
1671 var ids = YUQ('#review_members input');
1671 var ids = YUQ('#review_members input');
1672 for(var i=0; i<ids.length;i++){
1672 for(var i=0; i<ids.length;i++){
1673 var id = ids[i].value
1673 var id = ids[i].value
1674 reviewers_ids.push(id);
1674 reviewers_ids.push(id);
1675 }
1675 }
1676 }
1676 }
1677 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1677 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1678 "pull_request_id": pull_request_id});
1678 "pull_request_id": pull_request_id});
1679 var postData = {'_method':'put',
1679 var postData = {'_method':'put',
1680 'reviewers_ids': reviewers_ids};
1680 'reviewers_ids': reviewers_ids};
1681 var success = function(o){
1681 var success = function(o){
1682 window.location.reload();
1682 window.location.reload();
1683 }
1683 }
1684 ajaxPOST(url,postData,success);
1684 ajaxPOST(url,postData,success);
1685 }
1685 }
1686
1686
1687 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1687 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1688 var myUsers = users_list;
1688 var myUsers = users_list;
1689 var myGroups = groups_list;
1689 var myGroups = groups_list;
1690
1690
1691 // Define a custom search function for the DataSource of users
1691 // Define a custom search function for the DataSource of users
1692 var matchUsers = function (sQuery) {
1692 var matchUsers = function (sQuery) {
1693 // Case insensitive matching
1693 // Case insensitive matching
1694 var query = sQuery.toLowerCase();
1694 var query = sQuery.toLowerCase();
1695 var i = 0;
1695 var i = 0;
1696 var l = myUsers.length;
1696 var l = myUsers.length;
1697 var matches = [];
1697 var matches = [];
1698
1698
1699 // Match against each name of each contact
1699 // Match against each name of each contact
1700 for (; i < l; i++) {
1700 for (; i < l; i++) {
1701 contact = myUsers[i];
1701 contact = myUsers[i];
1702 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1702 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1703 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1703 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1704 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1704 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1705 matches[matches.length] = contact;
1705 matches[matches.length] = contact;
1706 }
1706 }
1707 }
1707 }
1708 return matches;
1708 return matches;
1709 };
1709 };
1710
1710
1711 // Define a custom search function for the DataSource of userGroups
1711 // Define a custom search function for the DataSource of userGroups
1712 var matchGroups = function (sQuery) {
1712 var matchGroups = function (sQuery) {
1713 // Case insensitive matching
1713 // Case insensitive matching
1714 var query = sQuery.toLowerCase();
1714 var query = sQuery.toLowerCase();
1715 var i = 0;
1715 var i = 0;
1716 var l = myGroups.length;
1716 var l = myGroups.length;
1717 var matches = [];
1717 var matches = [];
1718
1718
1719 // Match against each name of each contact
1719 // Match against each name of each contact
1720 for (; i < l; i++) {
1720 for (; i < l; i++) {
1721 matched_group = myGroups[i];
1721 matched_group = myGroups[i];
1722 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1722 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1723 matches[matches.length] = matched_group;
1723 matches[matches.length] = matched_group;
1724 }
1724 }
1725 }
1725 }
1726 return matches;
1726 return matches;
1727 };
1727 };
1728
1728
1729 //match all
1729 //match all
1730 var matchAll = function (sQuery) {
1730 var matchAll = function (sQuery) {
1731 u = matchUsers(sQuery);
1731 u = matchUsers(sQuery);
1732 return u
1732 return u
1733 };
1733 };
1734
1734
1735 // DataScheme for owner
1735 // DataScheme for owner
1736 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1736 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1737
1737
1738 ownerDS.responseSchema = {
1738 ownerDS.responseSchema = {
1739 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1739 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1740 };
1740 };
1741
1741
1742 // Instantiate AutoComplete for mentions
1742 // Instantiate AutoComplete for mentions
1743 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1743 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1744 reviewerAC.useShadow = false;
1744 reviewerAC.useShadow = false;
1745 reviewerAC.resultTypeList = false;
1745 reviewerAC.resultTypeList = false;
1746 reviewerAC.suppressInputUpdate = true;
1746 reviewerAC.suppressInputUpdate = true;
1747 reviewerAC.animVert = false;
1747 reviewerAC.animVert = false;
1748 reviewerAC.animHoriz = false;
1748 reviewerAC.animHoriz = false;
1749 reviewerAC.animSpeed = 0.1;
1749 reviewerAC.animSpeed = 0.1;
1750
1750
1751 // Helper highlight function for the formatter
1751 // Helper highlight function for the formatter
1752 var highlightMatch = function (full, snippet, matchindex) {
1752 var highlightMatch = function (full, snippet, matchindex) {
1753 return full.substring(0, matchindex)
1753 return full.substring(0, matchindex)
1754 + "<span class='match'>"
1754 + "<span class='match'>"
1755 + full.substr(matchindex, snippet.length)
1755 + full.substr(matchindex, snippet.length)
1756 + "</span>" + full.substring(matchindex + snippet.length);
1756 + "</span>" + full.substring(matchindex + snippet.length);
1757 };
1757 };
1758
1758
1759 // Custom formatter to highlight the matching letters
1759 // Custom formatter to highlight the matching letters
1760 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1760 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1761 var org_sQuery = sQuery;
1761 var org_sQuery = sQuery;
1762 if(this.dataSource.mentionQuery != null){
1762 if(this.dataSource.mentionQuery != null){
1763 sQuery = this.dataSource.mentionQuery;
1763 sQuery = this.dataSource.mentionQuery;
1764 }
1764 }
1765
1765
1766 var query = sQuery.toLowerCase();
1766 var query = sQuery.toLowerCase();
1767 var _gravatar = function(res, em, group){
1767 var _gravatar = function(res, em, group){
1768 if (group !== undefined){
1768 if (group !== undefined){
1769 em = '/images/icons/group.png'
1769 em = '/images/icons/group.png'
1770 }
1770 }
1771 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1771 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1772 return tmpl.format(em,res)
1772 return tmpl.format(em,res)
1773 }
1773 }
1774 if (oResultData.nname != undefined) {
1774 if (oResultData.nname != undefined) {
1775 var fname = oResultData.fname || "";
1775 var fname = oResultData.fname || "";
1776 var lname = oResultData.lname || "";
1776 var lname = oResultData.lname || "";
1777 var nname = oResultData.nname;
1777 var nname = oResultData.nname;
1778
1778
1779 // Guard against null value
1779 // Guard against null value
1780 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1780 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1781 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1781 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1782 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1782 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1783 displayfname, displaylname, displaynname;
1783 displayfname, displaylname, displaynname;
1784
1784
1785 if (fnameMatchIndex > -1) {
1785 if (fnameMatchIndex > -1) {
1786 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1786 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1787 } else {
1787 } else {
1788 displayfname = fname;
1788 displayfname = fname;
1789 }
1789 }
1790
1790
1791 if (lnameMatchIndex > -1) {
1791 if (lnameMatchIndex > -1) {
1792 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1792 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1793 } else {
1793 } else {
1794 displaylname = lname;
1794 displaylname = lname;
1795 }
1795 }
1796
1796
1797 if (nnameMatchIndex > -1) {
1797 if (nnameMatchIndex > -1) {
1798 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1798 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1799 } else {
1799 } else {
1800 displaynname = nname ? "(" + nname + ")" : "";
1800 displaynname = nname ? "(" + nname + ")" : "";
1801 }
1801 }
1802
1802
1803 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1803 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1804 } else {
1804 } else {
1805 return '';
1805 return '';
1806 }
1806 }
1807 };
1807 };
1808
1808
1809 //members cache to catch duplicates
1809 //members cache to catch duplicates
1810 reviewerAC.dataSource.cache = [];
1810 reviewerAC.dataSource.cache = [];
1811 // hack into select event
1811 // hack into select event
1812 if(reviewerAC.itemSelectEvent){
1812 if(reviewerAC.itemSelectEvent){
1813 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1813 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1814
1814
1815 var myAC = aArgs[0]; // reference back to the AC instance
1815 var myAC = aArgs[0]; // reference back to the AC instance
1816 var elLI = aArgs[1]; // reference to the selected LI element
1816 var elLI = aArgs[1]; // reference to the selected LI element
1817 var oData = aArgs[2]; // object literal of selected item's result data
1817 var oData = aArgs[2]; // object literal of selected item's result data
1818
1818
1819 //fill the autocomplete with value
1819 //fill the autocomplete with value
1820
1820
1821 if (oData.nname != undefined) {
1821 if (oData.nname != undefined) {
1822 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1822 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1823 oData.gravatar_lnk);
1823 oData.gravatar_lnk);
1824 myAC.dataSource.cache.push(oData.id);
1824 myAC.dataSource.cache.push(oData.id);
1825 YUD.get('user').value = ''
1825 YUD.get('user').value = ''
1826 }
1826 }
1827 });
1827 });
1828 }
1828 }
1829 return {
1829 return {
1830 ownerDS: ownerDS,
1830 ownerDS: ownerDS,
1831 reviewerAC: reviewerAC,
1831 reviewerAC: reviewerAC,
1832 };
1832 };
1833 }
1833 }
1834
1834
1835 /**
1835 /**
1836 * QUICK REPO MENU
1836 * QUICK REPO MENU
1837 */
1837 */
1838 var quick_repo_menu = function(){
1838 var quick_repo_menu = function(){
1839 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1839 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1840 var menu = e.currentTarget.firstElementChild.firstElementChild;
1840 var menu = e.currentTarget.firstElementChild.firstElementChild;
1841 if(YUD.hasClass(menu,'hidden')){
1841 if(YUD.hasClass(menu,'hidden')){
1842 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1842 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1843 YUD.replaceClass(menu, 'hidden', 'active');
1843 YUD.replaceClass(menu, 'hidden', 'active');
1844 }
1844 }
1845 })
1845 })
1846 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1846 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1847 var menu = e.currentTarget.firstElementChild.firstElementChild;
1847 var menu = e.currentTarget.firstElementChild.firstElementChild;
1848 if(YUD.hasClass(menu,'active')){
1848 if(YUD.hasClass(menu,'active')){
1849 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1849 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1850 YUD.replaceClass(menu, 'active', 'hidden');
1850 YUD.replaceClass(menu, 'active', 'hidden');
1851 }
1851 }
1852 })
1852 })
1853 };
1853 };
1854
1854
1855
1855
1856 /**
1856 /**
1857 * TABLE SORTING
1857 * TABLE SORTING
1858 */
1858 */
1859
1859
1860 // returns a node from given html;
1860 // returns a node from given html;
1861 var fromHTML = function(html){
1861 var fromHTML = function(html){
1862 var _html = document.createElement('element');
1862 var _html = document.createElement('element');
1863 _html.innerHTML = html;
1863 _html.innerHTML = html;
1864 return _html;
1864 return _html;
1865 }
1865 }
1866 var get_rev = function(node){
1866 var get_rev = function(node){
1867 var n = node.firstElementChild.firstElementChild;
1867 var n = node.firstElementChild.firstElementChild;
1868
1868
1869 if (n===null){
1869 if (n===null){
1870 return -1
1870 return -1
1871 }
1871 }
1872 else{
1872 else{
1873 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1873 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1874 return parseInt(out);
1874 return parseInt(out);
1875 }
1875 }
1876 }
1876 }
1877
1877
1878 var get_name = function(node){
1878 var get_name = function(node){
1879 var name = node.firstElementChild.children[2].innerHTML;
1879 var name = node.firstElementChild.children[2].innerHTML;
1880 return name
1880 return name
1881 }
1881 }
1882 var get_group_name = function(node){
1882 var get_group_name = function(node){
1883 var name = node.firstElementChild.children[1].innerHTML;
1883 var name = node.firstElementChild.children[1].innerHTML;
1884 return name
1884 return name
1885 }
1885 }
1886 var get_date = function(node){
1886 var get_date = function(node){
1887 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1887 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1888 return date_
1888 return date_
1889 }
1889 }
1890
1890
1891 var get_age = function(node){
1891 var get_age = function(node){
1892 return node
1892 return node
1893 }
1893 }
1894
1894
1895 var get_link = function(node){
1895 var get_link = function(node){
1896 return node.firstElementChild.text;
1896 return node.firstElementChild.text;
1897 }
1897 }
1898
1898
1899 var revisionSort = function(a, b, desc, field) {
1899 var revisionSort = function(a, b, desc, field) {
1900
1900
1901 var a_ = fromHTML(a.getData(field));
1901 var a_ = fromHTML(a.getData(field));
1902 var b_ = fromHTML(b.getData(field));
1902 var b_ = fromHTML(b.getData(field));
1903
1903
1904 // extract revisions from string nodes
1904 // extract revisions from string nodes
1905 a_ = get_rev(a_)
1905 a_ = get_rev(a_)
1906 b_ = get_rev(b_)
1906 b_ = get_rev(b_)
1907
1907
1908 var comp = YAHOO.util.Sort.compare;
1908 var comp = YAHOO.util.Sort.compare;
1909 var compState = comp(a_, b_, desc);
1909 var compState = comp(a_, b_, desc);
1910 return compState;
1910 return compState;
1911 };
1911 };
1912 var ageSort = function(a, b, desc, field) {
1912 var ageSort = function(a, b, desc, field) {
1913 var a_ = fromHTML(a.getData(field));
1913 var a_ = fromHTML(a.getData(field));
1914 var b_ = fromHTML(b.getData(field));
1914 var b_ = fromHTML(b.getData(field));
1915
1915
1916 // extract name from table
1916 // extract name from table
1917 a_ = get_date(a_)
1917 a_ = get_date(a_)
1918 b_ = get_date(b_)
1918 b_ = get_date(b_)
1919
1919
1920 var comp = YAHOO.util.Sort.compare;
1920 var comp = YAHOO.util.Sort.compare;
1921 var compState = comp(a_, b_, desc);
1921 var compState = comp(a_, b_, desc);
1922 return compState;
1922 return compState;
1923 };
1923 };
1924
1924
1925 var lastLoginSort = function(a, b, desc, field) {
1925 var lastLoginSort = function(a, b, desc, field) {
1926 var a_ = a.getData('last_login_raw') || 0;
1926 var a_ = a.getData('last_login_raw') || 0;
1927 var b_ = b.getData('last_login_raw') || 0;
1927 var b_ = b.getData('last_login_raw') || 0;
1928
1928
1929 var comp = YAHOO.util.Sort.compare;
1929 var comp = YAHOO.util.Sort.compare;
1930 var compState = comp(a_, b_, desc);
1930 var compState = comp(a_, b_, desc);
1931 return compState;
1931 return compState;
1932 };
1932 };
1933
1933
1934 var nameSort = function(a, b, desc, field) {
1934 var nameSort = function(a, b, desc, field) {
1935 var a_ = fromHTML(a.getData(field));
1935 var a_ = fromHTML(a.getData(field));
1936 var b_ = fromHTML(b.getData(field));
1936 var b_ = fromHTML(b.getData(field));
1937
1937
1938 // extract name from table
1938 // extract name from table
1939 a_ = get_name(a_)
1939 a_ = get_name(a_)
1940 b_ = get_name(b_)
1940 b_ = get_name(b_)
1941
1941
1942 var comp = YAHOO.util.Sort.compare;
1942 var comp = YAHOO.util.Sort.compare;
1943 var compState = comp(a_, b_, desc);
1943 var compState = comp(a_, b_, desc);
1944 return compState;
1944 return compState;
1945 };
1945 };
1946
1946
1947 var permNameSort = function(a, b, desc, field) {
1947 var permNameSort = function(a, b, desc, field) {
1948 var a_ = fromHTML(a.getData(field));
1948 var a_ = fromHTML(a.getData(field));
1949 var b_ = fromHTML(b.getData(field));
1949 var b_ = fromHTML(b.getData(field));
1950 // extract name from table
1950 // extract name from table
1951
1951
1952 a_ = a_.children[0].innerHTML;
1952 a_ = a_.children[0].innerHTML;
1953 b_ = b_.children[0].innerHTML;
1953 b_ = b_.children[0].innerHTML;
1954
1954
1955 var comp = YAHOO.util.Sort.compare;
1955 var comp = YAHOO.util.Sort.compare;
1956 var compState = comp(a_, b_, desc);
1956 var compState = comp(a_, b_, desc);
1957 return compState;
1957 return compState;
1958 };
1958 };
1959
1959
1960 var groupNameSort = function(a, b, desc, field) {
1960 var groupNameSort = function(a, b, desc, field) {
1961 var a_ = fromHTML(a.getData(field));
1961 var a_ = fromHTML(a.getData(field));
1962 var b_ = fromHTML(b.getData(field));
1962 var b_ = fromHTML(b.getData(field));
1963
1963
1964 // extract name from table
1964 // extract name from table
1965 a_ = get_group_name(a_)
1965 a_ = get_group_name(a_)
1966 b_ = get_group_name(b_)
1966 b_ = get_group_name(b_)
1967
1967
1968 var comp = YAHOO.util.Sort.compare;
1968 var comp = YAHOO.util.Sort.compare;
1969 var compState = comp(a_, b_, desc);
1969 var compState = comp(a_, b_, desc);
1970 return compState;
1970 return compState;
1971 };
1971 };
1972 var dateSort = function(a, b, desc, field) {
1972 var dateSort = function(a, b, desc, field) {
1973 var a_ = fromHTML(a.getData(field));
1973 var a_ = fromHTML(a.getData(field));
1974 var b_ = fromHTML(b.getData(field));
1974 var b_ = fromHTML(b.getData(field));
1975
1975
1976 // extract name from table
1976 // extract name from table
1977 a_ = get_date(a_)
1977 a_ = get_date(a_)
1978 b_ = get_date(b_)
1978 b_ = get_date(b_)
1979
1979
1980 var comp = YAHOO.util.Sort.compare;
1980 var comp = YAHOO.util.Sort.compare;
1981 var compState = comp(a_, b_, desc);
1981 var compState = comp(a_, b_, desc);
1982 return compState;
1982 return compState;
1983 };
1983 };
1984
1984
1985 var linkSort = function(a, b, desc, field) {
1985 var linkSort = function(a, b, desc, field) {
1986 var a_ = fromHTML(a.getData(field));
1986 var a_ = fromHTML(a.getData(field));
1987 var b_ = fromHTML(a.getData(field));
1987 var b_ = fromHTML(a.getData(field));
1988
1988
1989 // extract url text from string nodes
1989 // extract url text from string nodes
1990 a_ = get_link(a_)
1990 a_ = get_link(a_)
1991 b_ = get_link(b_)
1991 b_ = get_link(b_)
1992
1992
1993 var comp = YAHOO.util.Sort.compare;
1993 var comp = YAHOO.util.Sort.compare;
1994 var compState = comp(a_, b_, desc);
1994 var compState = comp(a_, b_, desc);
1995 return compState;
1995 return compState;
1996 }
1996 }
1997
1997
1998 var addPermAction = function(_html, users_list, groups_list){
1998 var addPermAction = function(_html, users_list, groups_list){
1999 var elmts = YUD.getElementsByClassName('last_new_member');
1999 var elmts = YUD.getElementsByClassName('last_new_member');
2000 var last_node = elmts[elmts.length-1];
2000 var last_node = elmts[elmts.length-1];
2001 if (last_node){
2001 if (last_node){
2002 var next_id = (YUD.getElementsByClassName('new_members')).length;
2002 var next_id = (YUD.getElementsByClassName('new_members')).length;
2003 _html = _html.format(next_id);
2003 _html = _html.format(next_id);
2004 last_node.innerHTML = _html;
2004 last_node.innerHTML = _html;
2005 YUD.setStyle(last_node, 'display', '');
2005 YUD.setStyle(last_node, 'display', '');
2006 YUD.removeClass(last_node, 'last_new_member');
2006 YUD.removeClass(last_node, 'last_new_member');
2007 MembersAutoComplete("perm_new_member_name_"+next_id,
2007 MembersAutoComplete("perm_new_member_name_"+next_id,
2008 "perm_container_"+next_id, users_list, groups_list);
2008 "perm_container_"+next_id, users_list, groups_list);
2009 //create new last NODE
2009 //create new last NODE
2010 var el = document.createElement('tr');
2010 var el = document.createElement('tr');
2011 el.id = 'add_perm_input';
2011 el.id = 'add_perm_input';
2012 YUD.addClass(el,'last_new_member');
2012 YUD.addClass(el,'last_new_member');
2013 YUD.addClass(el,'new_members');
2013 YUD.addClass(el,'new_members');
2014 YUD.insertAfter(el, last_node);
2014 YUD.insertAfter(el, last_node);
2015 }
2015 }
2016 }
2016 }
2017
2017
2018 /* Multi selectors */
2018 /* Multi selectors */
2019
2019
2020 var MultiSelectWidget = function(selected_id, available_id, form_id){
2020 var MultiSelectWidget = function(selected_id, available_id, form_id){
2021
2021
2022
2022
2023 //definition of containers ID's
2023 //definition of containers ID's
2024 var selected_container = selected_id;
2024 var selected_container = selected_id;
2025 var available_container = available_id;
2025 var available_container = available_id;
2026
2026
2027 //temp container for selected storage.
2027 //temp container for selected storage.
2028 var cache = new Array();
2028 var cache = new Array();
2029 var av_cache = new Array();
2029 var av_cache = new Array();
2030 var c = YUD.get(selected_container);
2030 var c = YUD.get(selected_container);
2031 var ac = YUD.get(available_container);
2031 var ac = YUD.get(available_container);
2032
2032
2033 //get only selected options for further fullfilment
2033 //get only selected options for further fullfilment
2034 for(var i = 0;node =c.options[i];i++){
2034 for(var i = 0;node =c.options[i];i++){
2035 if(node.selected){
2035 if(node.selected){
2036 //push selected to my temp storage left overs :)
2036 //push selected to my temp storage left overs :)
2037 cache.push(node);
2037 cache.push(node);
2038 }
2038 }
2039 }
2039 }
2040
2040
2041 //get all available options to cache
2041 //get all available options to cache
2042 for(var i = 0;node =ac.options[i];i++){
2042 for(var i = 0;node =ac.options[i];i++){
2043 //push selected to my temp storage left overs :)
2043 //push selected to my temp storage left overs :)
2044 av_cache.push(node);
2044 av_cache.push(node);
2045 }
2045 }
2046
2046
2047 //fill available only with those not in choosen
2047 //fill available only with those not in chosen
2048 ac.options.length=0;
2048 ac.options.length=0;
2049 tmp_cache = new Array();
2049 tmp_cache = new Array();
2050
2050
2051 for(var i = 0;node = av_cache[i];i++){
2051 for(var i = 0;node = av_cache[i];i++){
2052 var add = true;
2052 var add = true;
2053 for(var i2 = 0;node_2 = cache[i2];i2++){
2053 for(var i2 = 0;node_2 = cache[i2];i2++){
2054 if(node.value == node_2.value){
2054 if(node.value == node_2.value){
2055 add=false;
2055 add=false;
2056 break;
2056 break;
2057 }
2057 }
2058 }
2058 }
2059 if(add){
2059 if(add){
2060 tmp_cache.push(new Option(node.text, node.value, false, false));
2060 tmp_cache.push(new Option(node.text, node.value, false, false));
2061 }
2061 }
2062 }
2062 }
2063
2063
2064 for(var i = 0;node = tmp_cache[i];i++){
2064 for(var i = 0;node = tmp_cache[i];i++){
2065 ac.options[i] = node;
2065 ac.options[i] = node;
2066 }
2066 }
2067
2067
2068 function prompts_action_callback(e){
2068 function prompts_action_callback(e){
2069
2069
2070 var choosen = YUD.get(selected_container);
2070 var chosen = YUD.get(selected_container);
2071 var available = YUD.get(available_container);
2071 var available = YUD.get(available_container);
2072
2072
2073 //get checked and unchecked options from field
2073 //get checked and unchecked options from field
2074 function get_checked(from_field){
2074 function get_checked(from_field){
2075 //temp container for storage.
2075 //temp container for storage.
2076 var sel_cache = new Array();
2076 var sel_cache = new Array();
2077 var oth_cache = new Array();
2077 var oth_cache = new Array();
2078
2078
2079 for(var i = 0;node = from_field.options[i];i++){
2079 for(var i = 0;node = from_field.options[i];i++){
2080 if(node.selected){
2080 if(node.selected){
2081 //push selected fields :)
2081 //push selected fields :)
2082 sel_cache.push(node);
2082 sel_cache.push(node);
2083 }
2083 }
2084 else{
2084 else{
2085 oth_cache.push(node)
2085 oth_cache.push(node)
2086 }
2086 }
2087 }
2087 }
2088
2088
2089 return [sel_cache,oth_cache]
2089 return [sel_cache,oth_cache]
2090 }
2090 }
2091
2091
2092 //fill the field with given options
2092 //fill the field with given options
2093 function fill_with(field,options){
2093 function fill_with(field,options){
2094 //clear firtst
2094 //clear firtst
2095 field.options.length=0;
2095 field.options.length=0;
2096 for(var i = 0;node = options[i];i++){
2096 for(var i = 0;node = options[i];i++){
2097 field.options[i]=new Option(node.text, node.value,
2097 field.options[i]=new Option(node.text, node.value,
2098 false, false);
2098 false, false);
2099 }
2099 }
2100
2100
2101 }
2101 }
2102 //adds to current field
2102 //adds to current field
2103 function add_to(field,options){
2103 function add_to(field,options){
2104 for(var i = 0;node = options[i];i++){
2104 for(var i = 0;node = options[i];i++){
2105 field.appendChild(new Option(node.text, node.value,
2105 field.appendChild(new Option(node.text, node.value,
2106 false, false));
2106 false, false));
2107 }
2107 }
2108 }
2108 }
2109
2109
2110 // add action
2110 // add action
2111 if (this.id=='add_element'){
2111 if (this.id=='add_element'){
2112 var c = get_checked(available);
2112 var c = get_checked(available);
2113 add_to(choosen,c[0]);
2113 add_to(chosen,c[0]);
2114 fill_with(available,c[1]);
2114 fill_with(available,c[1]);
2115 }
2115 }
2116 // remove action
2116 // remove action
2117 if (this.id=='remove_element'){
2117 if (this.id=='remove_element'){
2118 var c = get_checked(choosen);
2118 var c = get_checked(chosen);
2119 add_to(available,c[0]);
2119 add_to(available,c[0]);
2120 fill_with(choosen,c[1]);
2120 fill_with(chosen,c[1]);
2121 }
2121 }
2122 // add all elements
2122 // add all elements
2123 if(this.id=='add_all_elements'){
2123 if(this.id=='add_all_elements'){
2124 for(var i=0; node = available.options[i];i++){
2124 for(var i=0; node = available.options[i];i++){
2125 choosen.appendChild(new Option(node.text,
2125 chosen.appendChild(new Option(node.text,
2126 node.value, false, false));
2126 node.value, false, false));
2127 }
2127 }
2128 available.options.length = 0;
2128 available.options.length = 0;
2129 }
2129 }
2130 //remove all elements
2130 //remove all elements
2131 if(this.id=='remove_all_elements'){
2131 if(this.id=='remove_all_elements'){
2132 for(var i=0; node = choosen.options[i];i++){
2132 for(var i=0; node = chosen.options[i];i++){
2133 available.appendChild(new Option(node.text,
2133 available.appendChild(new Option(node.text,
2134 node.value, false, false));
2134 node.value, false, false));
2135 }
2135 }
2136 choosen.options.length = 0;
2136 chosen.options.length = 0;
2137 }
2137 }
2138
2138
2139 }
2139 }
2140
2140
2141 YUE.addListener(['add_element','remove_element',
2141 YUE.addListener(['add_element','remove_element',
2142 'add_all_elements','remove_all_elements'],'click',
2142 'add_all_elements','remove_all_elements'],'click',
2143 prompts_action_callback)
2143 prompts_action_callback)
2144 if (form_id !== undefined) {
2144 if (form_id !== undefined) {
2145 YUE.addListener(form_id,'submit',function(){
2145 YUE.addListener(form_id,'submit',function(){
2146 var choosen = YUD.get(selected_container);
2146 var chosen = YUD.get(selected_container);
2147 for (var i = 0; i < choosen.options.length; i++) {
2147 for (var i = 0; i < chosen.options.length; i++) {
2148 choosen.options[i].selected = 'selected';
2148 chosen.options[i].selected = 'selected';
2149 }
2149 }
2150 });
2150 });
2151 }
2151 }
2152 }
2152 }
2153
2153
2154
2154
2155 // global hooks after DOM is loaded
2155 // global hooks after DOM is loaded
2156
2156
2157 YUE.onDOMReady(function(){
2157 YUE.onDOMReady(function(){
2158 YUE.on(YUQ('.diff-collapse-button'), 'click', function(e){
2158 YUE.on(YUQ('.diff-collapse-button'), 'click', function(e){
2159 var button = e.currentTarget;
2159 var button = e.currentTarget;
2160 var t = YUD.get(button).getAttribute('target');
2160 var t = YUD.get(button).getAttribute('target');
2161 console.log(t);
2161 console.log(t);
2162 if(YUD.hasClass(t, 'hidden')){
2162 if(YUD.hasClass(t, 'hidden')){
2163 YUD.removeClass(t, 'hidden');
2163 YUD.removeClass(t, 'hidden');
2164 YUD.get(button).innerHTML = "&uarr; {0} &uarr;".format(_TM['Collapse diff']);
2164 YUD.get(button).innerHTML = "&uarr; {0} &uarr;".format(_TM['Collapse diff']);
2165 }
2165 }
2166 else if(!YUD.hasClass(t, 'hidden')){
2166 else if(!YUD.hasClass(t, 'hidden')){
2167 YUD.addClass(t, 'hidden');
2167 YUD.addClass(t, 'hidden');
2168 YUD.get(button).innerHTML = "&darr; {0} &darr;".format(_TM['Expand diff']);
2168 YUD.get(button).innerHTML = "&darr; {0} &darr;".format(_TM['Expand diff']);
2169 }
2169 }
2170 });
2170 });
2171
2171
2172
2172
2173
2173
2174 });
2174 });
2175
2175
@@ -1,215 +1,215 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 ${_('Permissions administration')} &middot; ${c.rhodecode_name}
5 ${_('Permissions administration')} &middot; ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${_('permissions')}
11 ${_('permissions')}
12 </%def>
12 </%def>
13
13
14 <%def name="page_nav()">
14 <%def name="page_nav()">
15 ${self.menu('admin')}
15 ${self.menu('admin')}
16 </%def>
16 </%def>
17
17
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box box-left">
19 <div class="box box-left">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24 <h3>${_('Default permissions')}</h3>
24 <h3>${_('Default permissions')}</h3>
25 ${h.form(url('permission', id='default'),method='put')}
25 ${h.form(url('permission', id='default'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28 <div class="fields">
28 <div class="fields">
29 <div class="field">
29 <div class="field">
30 <div class="label label-checkbox">
30 <div class="label label-checkbox">
31 <label for="anonymous">${_('Anonymous access')}:</label>
31 <label for="anonymous">${_('Anonymous access')}:</label>
32 </div>
32 </div>
33 <div class="checkboxes">
33 <div class="checkboxes">
34 <div class="checkbox">
34 <div class="checkbox">
35 ${h.checkbox('anonymous',True)}
35 ${h.checkbox('anonymous',True)}
36 </div>
36 </div>
37 </div>
37 </div>
38 </div>
38 </div>
39 <div class="field">
39 <div class="field">
40 <div class="label">
40 <div class="label">
41 <label for="default_repo_perm">${_('Repository')}:</label>
41 <label for="default_repo_perm">${_('Repository')}:</label>
42 </div>
42 </div>
43 <div class="select">
43 <div class="select">
44 ${h.select('default_repo_perm','',c.repo_perms_choices)}
44 ${h.select('default_repo_perm','',c.repo_perms_choices)}
45
45
46 ${h.checkbox('overwrite_default_repo','true')}
46 ${h.checkbox('overwrite_default_repo','true')}
47 <label for="overwrite_default_repo">
47 <label for="overwrite_default_repo">
48 <span class="tooltip"
48 <span class="tooltip"
49 title="${h.tooltip(_('All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost'))}">
49 title="${h.tooltip(_('All default permissions on each repository will be reset to chosen permission, note that all custom default permission on repositories will be lost'))}">
50 ${_('overwrite existing settings')}</span> </label>
50 ${_('overwrite existing settings')}</span> </label>
51 </div>
51 </div>
52 </div>
52 </div>
53 <div class="field">
53 <div class="field">
54 <div class="label">
54 <div class="label">
55 <label for="default_group_perm">${_('Repository group')}:</label>
55 <label for="default_group_perm">${_('Repository group')}:</label>
56 </div>
56 </div>
57 <div class="select">
57 <div class="select">
58 ${h.select('default_group_perm','',c.group_perms_choices)}
58 ${h.select('default_group_perm','',c.group_perms_choices)}
59 ${h.checkbox('overwrite_default_group','true')}
59 ${h.checkbox('overwrite_default_group','true')}
60 <label for="overwrite_default_group">
60 <label for="overwrite_default_group">
61 <span class="tooltip"
61 <span class="tooltip"
62 title="${h.tooltip(_('All default permissions on each repository group will be reset to choosen permission, note that all custom default permission on repository groups will be lost'))}">
62 title="${h.tooltip(_('All default permissions on each repository group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
63 ${_('overwrite existing settings')}</span> </label>
63 ${_('overwrite existing settings')}</span> </label>
64
64
65 </div>
65 </div>
66 </div>
66 </div>
67 <div class="field">
67 <div class="field">
68 <div class="label">
68 <div class="label">
69 <label for="default_register">${_('Registration')}:</label>
69 <label for="default_register">${_('Registration')}:</label>
70 </div>
70 </div>
71 <div class="select">
71 <div class="select">
72 ${h.select('default_register','',c.register_choices)}
72 ${h.select('default_register','',c.register_choices)}
73 </div>
73 </div>
74 </div>
74 </div>
75 <div class="field">
75 <div class="field">
76 <div class="label">
76 <div class="label">
77 <label for="default_create">${_('Repository creation')}:</label>
77 <label for="default_create">${_('Repository creation')}:</label>
78 </div>
78 </div>
79 <div class="select">
79 <div class="select">
80 ${h.select('default_create','',c.create_choices)}
80 ${h.select('default_create','',c.create_choices)}
81 </div>
81 </div>
82 </div>
82 </div>
83 <div class="field">
83 <div class="field">
84 <div class="label">
84 <div class="label">
85 <label for="default_fork">${_('Repository forking')}:</label>
85 <label for="default_fork">${_('Repository forking')}:</label>
86 </div>
86 </div>
87 <div class="select">
87 <div class="select">
88 ${h.select('default_fork','',c.fork_choices)}
88 ${h.select('default_fork','',c.fork_choices)}
89 </div>
89 </div>
90 </div>
90 </div>
91 <div class="buttons">
91 <div class="buttons">
92 ${h.submit('save',_('Save'),class_="ui-btn large")}
92 ${h.submit('save',_('Save'),class_="ui-btn large")}
93 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
93 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
94 </div>
94 </div>
95 </div>
95 </div>
96 </div>
96 </div>
97 ${h.end_form()}
97 ${h.end_form()}
98 </div>
98 </div>
99
99
100 <div style="min-height:780px" class="box box-right">
100 <div style="min-height:780px" class="box box-right">
101 <!-- box / title -->
101 <!-- box / title -->
102 <div class="title">
102 <div class="title">
103 <h5>${_('Default User Permissions')}</h5>
103 <h5>${_('Default User Permissions')}</h5>
104 </div>
104 </div>
105
105
106 ## permissions overview
106 ## permissions overview
107 <div id="perms" class="table">
107 <div id="perms" class="table">
108 %for section in sorted(c.perm_user.permissions.keys()):
108 %for section in sorted(c.perm_user.permissions.keys()):
109 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
109 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
110 %if not c.perm_user.permissions[section]:
110 %if not c.perm_user.permissions[section]:
111 <span class="empty_data">${_('Nothing here yet')}</span>
111 <span class="empty_data">${_('Nothing here yet')}</span>
112 %else:
112 %else:
113 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
113 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
114 <table id="tbl_list_${section}">
114 <table id="tbl_list_${section}">
115 <thead>
115 <thead>
116 <tr>
116 <tr>
117 <th class="left">${_('Name')}</th>
117 <th class="left">${_('Name')}</th>
118 <th class="left">${_('Permission')}</th>
118 <th class="left">${_('Permission')}</th>
119 <th class="left">${_('Edit Permission')}</th>
119 <th class="left">${_('Edit Permission')}</th>
120 </thead>
120 </thead>
121 <tbody>
121 <tbody>
122 %for k in c.perm_user.permissions[section]:
122 %for k in c.perm_user.permissions[section]:
123 <%
123 <%
124 if section != 'global':
124 if section != 'global':
125 section_perm = c.perm_user.permissions[section].get(k)
125 section_perm = c.perm_user.permissions[section].get(k)
126 _perm = section_perm.split('.')[-1]
126 _perm = section_perm.split('.')[-1]
127 else:
127 else:
128 _perm = section_perm = None
128 _perm = section_perm = None
129 %>
129 %>
130 <tr>
130 <tr>
131 <td>
131 <td>
132 %if section == 'repositories':
132 %if section == 'repositories':
133 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
133 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
134 %elif section == 'repositories_groups':
134 %elif section == 'repositories_groups':
135 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
135 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
136 %else:
136 %else:
137 ${h.get_permission_name(k)}
137 ${h.get_permission_name(k)}
138 %endif
138 %endif
139 </td>
139 </td>
140 <td>
140 <td>
141 %if section == 'global':
141 %if section == 'global':
142 ${h.bool2icon(k.split('.')[-1] != 'none')}
142 ${h.bool2icon(k.split('.')[-1] != 'none')}
143 %else:
143 %else:
144 <span class="perm_tag ${_perm}">${section_perm}</span>
144 <span class="perm_tag ${_perm}">${section_perm}</span>
145 %endif
145 %endif
146 </td>
146 </td>
147 <td>
147 <td>
148 %if section == 'repositories':
148 %if section == 'repositories':
149 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
149 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
150 %elif section == 'repositories_groups':
150 %elif section == 'repositories_groups':
151 <a href="${h.url('edit_repos_group',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
151 <a href="${h.url('edit_repos_group',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
152 %else:
152 %else:
153 --
153 --
154 %endif
154 %endif
155 </td>
155 </td>
156 </tr>
156 </tr>
157 %endfor
157 %endfor
158 </tbody>
158 </tbody>
159 </table>
159 </table>
160 </div>
160 </div>
161 %endif
161 %endif
162 %endfor
162 %endfor
163 </div>
163 </div>
164 </div>
164 </div>
165 <div class="box box-left" style="clear:left">
165 <div class="box box-left" style="clear:left">
166 <!-- box / title -->
166 <!-- box / title -->
167 <div class="title">
167 <div class="title">
168 <h5>${_('Allowed IP addresses')}</h5>
168 <h5>${_('Allowed IP addresses')}</h5>
169 </div>
169 </div>
170
170
171 <div class="ips_wrap">
171 <div class="ips_wrap">
172 <table class="noborder">
172 <table class="noborder">
173 %if c.user_ip_map:
173 %if c.user_ip_map:
174 %for ip in c.user_ip_map:
174 %for ip in c.user_ip_map:
175 <tr>
175 <tr>
176 <td><div class="ip">${ip.ip_addr}</div></td>
176 <td><div class="ip">${ip.ip_addr}</div></td>
177 <td><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
177 <td><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
178 <td>
178 <td>
179 ${h.form(url('user_ips_delete', id=c.user.user_id),method='delete')}
179 ${h.form(url('user_ips_delete', id=c.user.user_id),method='delete')}
180 ${h.hidden('del_ip',ip.ip_id)}
180 ${h.hidden('del_ip',ip.ip_id)}
181 ${h.hidden('default_user', 'True')}
181 ${h.hidden('default_user', 'True')}
182 ${h.submit('remove_',_('delete'),id="remove_ip_%s" % ip.ip_id,
182 ${h.submit('remove_',_('delete'),id="remove_ip_%s" % ip.ip_id,
183 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
183 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
184 ${h.end_form()}
184 ${h.end_form()}
185 </td>
185 </td>
186 </tr>
186 </tr>
187 %endfor
187 %endfor
188 %else:
188 %else:
189 <tr><td><div class="ip">${_('All IP addresses are allowed')}</div></td></tr>
189 <tr><td><div class="ip">${_('All IP addresses are allowed')}</div></td></tr>
190 %endif
190 %endif
191 </table>
191 </table>
192 </div>
192 </div>
193
193
194 ${h.form(url('user_ips', id=c.user.user_id),method='put')}
194 ${h.form(url('user_ips', id=c.user.user_id),method='put')}
195 <div class="form">
195 <div class="form">
196 <!-- fields -->
196 <!-- fields -->
197 <div class="fields">
197 <div class="fields">
198 <div class="field">
198 <div class="field">
199 <div class="label">
199 <div class="label">
200 <label for="new_ip">${_('New ip address')}:</label>
200 <label for="new_ip">${_('New ip address')}:</label>
201 </div>
201 </div>
202 <div class="input">
202 <div class="input">
203 ${h.hidden('default_user', 'True')}
203 ${h.hidden('default_user', 'True')}
204 ${h.text('new_ip', class_='medium')}
204 ${h.text('new_ip', class_='medium')}
205 </div>
205 </div>
206 </div>
206 </div>
207 <div class="buttons">
207 <div class="buttons">
208 ${h.submit('save',_('Add'),class_="ui-btn large")}
208 ${h.submit('save',_('Add'),class_="ui-btn large")}
209 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
209 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
210 </div>
210 </div>
211 </div>
211 </div>
212 </div>
212 </div>
213 ${h.end_form()}
213 ${h.end_form()}
214 </div>
214 </div>
215 </%def>
215 </%def>
@@ -1,228 +1,228 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 ${_('Edit user group')} ${c.users_group.users_group_name} &middot; ${c.rhodecode_name}
5 ${_('Edit user group')} ${c.users_group.users_group_name} &middot; ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('UserGroups'),h.url('users_groups'))}
11 ${h.link_to(_('UserGroups'),h.url('users_groups'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.users_group.users_group_name}"
13 ${_('edit')} "${c.users_group.users_group_name}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <!-- end box / title -->
26 <!-- end box / title -->
27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
28 <div class="form">
28 <div class="form">
29 <!-- fields -->
29 <!-- fields -->
30 <div class="fields">
30 <div class="fields">
31 <div class="field">
31 <div class="field">
32 <div class="label">
32 <div class="label">
33 <label for="users_group_name">${_('Group name')}:</label>
33 <label for="users_group_name">${_('Group name')}:</label>
34 </div>
34 </div>
35 <div class="input">
35 <div class="input">
36 ${h.text('users_group_name',class_='small')}
36 ${h.text('users_group_name',class_='small')}
37 </div>
37 </div>
38 </div>
38 </div>
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label label-checkbox">
41 <div class="label label-checkbox">
42 <label for="users_group_active">${_('Active')}:</label>
42 <label for="users_group_active">${_('Active')}:</label>
43 </div>
43 </div>
44 <div class="checkboxes">
44 <div class="checkboxes">
45 ${h.checkbox('users_group_active',value=True)}
45 ${h.checkbox('users_group_active',value=True)}
46 </div>
46 </div>
47 </div>
47 </div>
48 <div class="field">
48 <div class="field">
49 <div class="label">
49 <div class="label">
50 <label for="users_group_active">${_('Members')}:</label>
50 <label for="users_group_active">${_('Members')}:</label>
51 </div>
51 </div>
52 <div class="select">
52 <div class="select">
53 <table>
53 <table>
54 <tr>
54 <tr>
55 <td>
55 <td>
56 <div>
56 <div>
57 <div style="float:left">
57 <div style="float:left">
58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
58 <div class="text" style="padding: 0px 0px 6px;">${_('Chosen group members')}</div>
59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
61 ${_('Remove all elements')}
61 ${_('Remove all elements')}
62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
63 </div>
63 </div>
64 </div>
64 </div>
65 <div style="float:left;width:20px;padding-top:50px">
65 <div style="float:left;width:20px;padding-top:50px">
66 <img alt="add" id="add_element"
66 <img alt="add" id="add_element"
67 style="padding:2px;cursor:pointer"
67 style="padding:2px;cursor:pointer"
68 src="${h.url('/images/icons/arrow_left.png')}"/>
68 src="${h.url('/images/icons/arrow_left.png')}"/>
69 <br />
69 <br />
70 <img alt="remove" id="remove_element"
70 <img alt="remove" id="remove_element"
71 style="padding:2px;cursor:pointer"
71 style="padding:2px;cursor:pointer"
72 src="${h.url('/images/icons/arrow_right.png')}"/>
72 src="${h.url('/images/icons/arrow_right.png')}"/>
73 </div>
73 </div>
74 <div style="float:left">
74 <div style="float:left">
75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
79 ${_('Add all elements')}
79 ${_('Add all elements')}
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83 </td>
83 </td>
84 </tr>
84 </tr>
85 </table>
85 </table>
86 </div>
86 </div>
87
87
88 </div>
88 </div>
89 <div class="buttons">
89 <div class="buttons">
90 ${h.submit('save',_('save'),class_="ui-btn large")}
90 ${h.submit('save',_('save'),class_="ui-btn large")}
91 </div>
91 </div>
92 </div>
92 </div>
93 </div>
93 </div>
94 ${h.end_form()}
94 ${h.end_form()}
95 </div>
95 </div>
96
96
97 <div class="box box-right">
97 <div class="box box-right">
98 <!-- box / title -->
98 <!-- box / title -->
99 <div class="title">
99 <div class="title">
100 <h5>${_('Permissions')}</h5>
100 <h5>${_('Permissions')}</h5>
101 </div>
101 </div>
102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
103 <div class="form">
103 <div class="form">
104 <!-- fields -->
104 <!-- fields -->
105 <div class="fields">
105 <div class="fields">
106 <div class="field">
106 <div class="field">
107 <div class="label label-checkbox">
107 <div class="label label-checkbox">
108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
109 </div>
109 </div>
110 <div class="checkboxes">
110 <div class="checkboxes">
111 ${h.checkbox('inherit_default_permissions',value=True)}
111 ${h.checkbox('inherit_default_permissions',value=True)}
112 </div>
112 </div>
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
115 </div>
115 </div>
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
117 <div class="field">
117 <div class="field">
118 <div class="label label-checkbox">
118 <div class="label label-checkbox">
119 <label for="create_repo_perm">${_('Create repositories')}:</label>
119 <label for="create_repo_perm">${_('Create repositories')}:</label>
120 </div>
120 </div>
121 <div class="checkboxes">
121 <div class="checkboxes">
122 ${h.checkbox('create_repo_perm',value=True)}
122 ${h.checkbox('create_repo_perm',value=True)}
123 </div>
123 </div>
124 </div>
124 </div>
125 <div class="field">
125 <div class="field">
126 <div class="label label-checkbox">
126 <div class="label label-checkbox">
127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
128 </div>
128 </div>
129 <div class="checkboxes">
129 <div class="checkboxes">
130 ${h.checkbox('fork_repo_perm',value=True)}
130 ${h.checkbox('fork_repo_perm',value=True)}
131 </div>
131 </div>
132 </div>
132 </div>
133 </div>
133 </div>
134 <div class="buttons">
134 <div class="buttons">
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
137 </div>
137 </div>
138 </div>
138 </div>
139 </div>
139 </div>
140 ${h.end_form()}
140 ${h.end_form()}
141 </div>
141 </div>
142
142
143 <div class="box box-right">
143 <div class="box box-right">
144 <!-- box / title -->
144 <!-- box / title -->
145 <div class="title">
145 <div class="title">
146 <h5>${_('Group members')}</h5>
146 <h5>${_('Group members')}</h5>
147 </div>
147 </div>
148
148
149 <div class="group_members_wrap">
149 <div class="group_members_wrap">
150 % if c.group_members_obj:
150 % if c.group_members_obj:
151 <ul class="group_members">
151 <ul class="group_members">
152 %for user in c.group_members_obj:
152 %for user in c.group_members_obj:
153 <li>
153 <li>
154 <div class="group_member">
154 <div class="group_member">
155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
157 <div>${user.full_name}</div>
157 <div>${user.full_name}</div>
158 </div>
158 </div>
159 </li>
159 </li>
160 %endfor
160 %endfor
161 </ul>
161 </ul>
162 %else:
162 %else:
163 <span class="empty_data">${_('No members yet')}</span>
163 <span class="empty_data">${_('No members yet')}</span>
164 %endif
164 %endif
165 </div>
165 </div>
166 </div>
166 </div>
167
167
168 <div class="box box-left">
168 <div class="box box-left">
169 <!-- box / title -->
169 <!-- box / title -->
170 <div class="title">
170 <div class="title">
171 <h5>${_('Permissions defined for this group')}</h5>
171 <h5>${_('Permissions defined for this group')}</h5>
172 </div>
172 </div>
173 ## permissions overview
173 ## permissions overview
174 <div id="perms" class="table">
174 <div id="perms" class="table">
175 %for section in sorted(c.users_group.permissions.keys()):
175 %for section in sorted(c.users_group.permissions.keys()):
176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
177 %if not c.users_group.permissions:
177 %if not c.users_group.permissions:
178 <span class="empty_data">${_('No permissions set yet')}</span>
178 <span class="empty_data">${_('No permissions set yet')}</span>
179 %else:
179 %else:
180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
181 <table id="tbl_list_repository">
181 <table id="tbl_list_repository">
182 <thead>
182 <thead>
183 <tr>
183 <tr>
184 <th class="left">${_('Name')}</th>
184 <th class="left">${_('Name')}</th>
185 <th class="left">${_('Permission')}</th>
185 <th class="left">${_('Permission')}</th>
186 <th class="left">${_('Edit Permission')}</th>
186 <th class="left">${_('Edit Permission')}</th>
187 </thead>
187 </thead>
188 <tbody>
188 <tbody>
189 %for k in c.users_group.permissions[section]:
189 %for k in c.users_group.permissions[section]:
190 <%
190 <%
191 section_perm = c.users_group.permissions[section].get(k)
191 section_perm = c.users_group.permissions[section].get(k)
192 _perm = section_perm.split('.')[-1]
192 _perm = section_perm.split('.')[-1]
193 %>
193 %>
194 <tr>
194 <tr>
195 <td>
195 <td>
196 %if section == 'repositories':
196 %if section == 'repositories':
197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
198 %elif section == 'repositories_groups':
198 %elif section == 'repositories_groups':
199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
200 %endif
200 %endif
201 </td>
201 </td>
202 <td>
202 <td>
203 <span class="perm_tag ${_perm}">${section_perm}</span>
203 <span class="perm_tag ${_perm}">${section_perm}</span>
204 </td>
204 </td>
205 <td>
205 <td>
206 %if section == 'repositories':
206 %if section == 'repositories':
207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
208 %elif section == 'repositories_groups':
208 %elif section == 'repositories_groups':
209 <a href="${h.url('edit_repos_group',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
209 <a href="${h.url('edit_repos_group',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
210 %else:
210 %else:
211 --
211 --
212 %endif
212 %endif
213 </td>
213 </td>
214 </tr>
214 </tr>
215 %endfor
215 %endfor
216 </tbody>
216 </tbody>
217 </table>
217 </table>
218 </div>
218 </div>
219 %endif
219 %endif
220 %endfor
220 %endfor
221 </div>
221 </div>
222 </div>
222 </div>
223
223
224
224
225 <script type="text/javascript">
225 <script type="text/javascript">
226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
227 </script>
227 </script>
228 </%def>
228 </%def>
General Comments 0
You need to be logged in to leave comments. Login now