##// END OF EJS Templates
auth-plugins: updated UI for authentication plugins, and allow unsorted (in order of registration) display of plugins.
marcink -
r3233:4c12f36b default
parent child Browse files
Show More
@@ -1,150 +1,155 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import collections
22
23
23 from pyramid.exceptions import ConfigurationError
24 from pyramid.exceptions import ConfigurationError
24
25
25 from rhodecode.lib.utils2 import safe_str
26 from rhodecode.lib.utils2 import safe_str
26 from rhodecode.model.settings import SettingsModel
27 from rhodecode.model.settings import SettingsModel
27 from rhodecode.translation import _
28 from rhodecode.translation import _
28
29
29
30
30 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
31
32
32
33
33 class AuthnResourceBase(object):
34 class AuthnResourceBase(object):
34 __name__ = None
35 __name__ = None
35 __parent__ = None
36 __parent__ = None
36
37
37 def get_root(self):
38 def get_root(self):
38 current = self
39 current = self
39 while current.__parent__ is not None:
40 while current.__parent__ is not None:
40 current = current.__parent__
41 current = current.__parent__
41 return current
42 return current
42
43
43
44
44 class AuthnPluginResourceBase(AuthnResourceBase):
45 class AuthnPluginResourceBase(AuthnResourceBase):
45
46
46 def __init__(self, plugin):
47 def __init__(self, plugin):
47 self.plugin = plugin
48 self.plugin = plugin
48 self.__name__ = plugin.get_url_slug()
49 self.__name__ = plugin.get_url_slug()
49 self.display_name = plugin.get_display_name()
50 self.display_name = plugin.get_display_name()
50
51
51
52
52 class AuthnRootResource(AuthnResourceBase):
53 class AuthnRootResource(AuthnResourceBase):
53 """
54 """
54 This is the root traversal resource object for the authentication settings.
55 This is the root traversal resource object for the authentication settings.
55 """
56 """
56
57
57 def __init__(self):
58 def __init__(self):
58 self._store = {}
59 self._store = collections.OrderedDict()
59 self._resource_name_map = {}
60 self._resource_name_map = {}
60 self.display_name = _('Global')
61 self.display_name = _('Global')
61
62
62 def __getitem__(self, key):
63 def __getitem__(self, key):
63 """
64 """
64 Customized get item function to return only items (plugins) that are
65 Customized get item function to return only items (plugins) that are
65 activated.
66 activated.
66 """
67 """
67 if self._is_item_active(key):
68 if self._is_item_active(key):
68 return self._store[key]
69 return self._store[key]
69 else:
70 else:
70 raise KeyError('Authentication plugin "{}" is not active.'.format(
71 raise KeyError('Authentication plugin "{}" is not active.'.format(
71 key))
72 key))
72
73
73 def __iter__(self):
74 def __iter__(self):
74 for key in self._store.keys():
75 for key in self._store.keys():
75 if self._is_item_active(key):
76 if self._is_item_active(key):
76 yield self._store[key]
77 yield self._store[key]
77
78
78 def _is_item_active(self, key):
79 def _is_item_active(self, key):
79 activated_plugins = SettingsModel().get_auth_plugins()
80 activated_plugins = SettingsModel().get_auth_plugins()
80 plugin_id = self.get_plugin_id(key)
81 plugin_id = self.get_plugin_id(key)
81 return plugin_id in activated_plugins
82 return plugin_id in activated_plugins
82
83
83 def get_plugin_id(self, resource_name):
84 def get_plugin_id(self, resource_name):
84 """
85 """
85 Return the plugin id for the given traversal resource name.
86 Return the plugin id for the given traversal resource name.
86 """
87 """
87 # TODO: Store this info in the resource element.
88 # TODO: Store this info in the resource element.
88 return self._resource_name_map[resource_name]
89 return self._resource_name_map[resource_name]
89
90
90 def get_sorted_list(self):
91 def get_sorted_list(self):
91 """
92 """
92 Returns a sorted list of sub resources for displaying purposes.
93 Returns a sorted list of sub resources for displaying purposes.
93 """
94 """
94 def sort_key(resource):
95 def sort_key(resource):
95 return str.lower(safe_str(resource.display_name))
96 return str.lower(safe_str(resource.display_name))
96
97
97 active = [item for item in self]
98 active = [item for item in self]
98 return sorted(active, key=sort_key)
99 return sorted(active, key=sort_key)
99
100
100 def get_nav_list(self):
101 def get_nav_list(self, sort=True):
101 """
102 """
102 Returns a sorted list of resources for displaying the navigation.
103 Returns a sorted list of resources for displaying the navigation.
103 """
104 """
104 list = self.get_sorted_list()
105 if sort:
105 list.insert(0, self)
106 nav_list = self.get_sorted_list()
106 return list
107 else:
108 nav_list = [item for item in self]
109
110 nav_list.insert(0, self)
111 return nav_list
107
112
108 def add_authn_resource(self, config, plugin_id, resource):
113 def add_authn_resource(self, config, plugin_id, resource):
109 """
114 """
110 Register a traversal resource as a sub element to the authentication
115 Register a traversal resource as a sub element to the authentication
111 settings. This method is registered as a directive on the pyramid
116 settings. This method is registered as a directive on the pyramid
112 configurator object and called by plugins.
117 configurator object and called by plugins.
113 """
118 """
114
119
115 def _ensure_unique_name(name, limit=100):
120 def _ensure_unique_name(name, limit=100):
116 counter = 1
121 counter = 1
117 current = name
122 current = name
118 while current in self._store.keys():
123 while current in self._store.keys():
119 current = '{}{}'.format(name, counter)
124 current = '{}{}'.format(name, counter)
120 counter += 1
125 counter += 1
121 if counter > limit:
126 if counter > limit:
122 raise ConfigurationError(
127 raise ConfigurationError(
123 'Cannot build unique name for traversal resource "%s" '
128 'Cannot build unique name for traversal resource "%s" '
124 'registered by plugin "%s"', name, plugin_id)
129 'registered by plugin "%s"', name, plugin_id)
125 return current
130 return current
126
131
127 # Allow plugin resources with identical names by rename duplicates.
132 # Allow plugin resources with identical names by rename duplicates.
128 unique_name = _ensure_unique_name(resource.__name__)
133 unique_name = _ensure_unique_name(resource.__name__)
129 if unique_name != resource.__name__:
134 if unique_name != resource.__name__:
130 log.warn('Name collision for traversal resource "%s" registered '
135 log.warn('Name collision for traversal resource "%s" registered '
131 'by authentication plugin "%s"', resource.__name__,
136 'by authentication plugin "%s"', resource.__name__,
132 plugin_id)
137 plugin_id)
133 resource.__name__ = unique_name
138 resource.__name__ = unique_name
134
139
135 log.debug('Register traversal resource "%s" for plugin "%s"',
140 log.debug('Register traversal resource "%s" for plugin "%s"',
136 unique_name, plugin_id)
141 unique_name, plugin_id)
137 self._resource_name_map[unique_name] = plugin_id
142 self._resource_name_map[unique_name] = plugin_id
138 resource.__parent__ = self
143 resource.__parent__ = self
139 self._store[unique_name] = resource
144 self._store[unique_name] = resource
140
145
141
146
142 root = AuthnRootResource()
147 root = AuthnRootResource()
143
148
144
149
145 def root_factory(request=None):
150 def root_factory(request=None):
146 """
151 """
147 Returns the root traversal resource instance used for the authentication
152 Returns the root traversal resource instance used for the authentication
148 settings route.
153 settings route.
149 """
154 """
150 return root
155 return root
@@ -1,118 +1,124 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Authentication Settings')}
5 ${_('Authentication Settings')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}}
7 &middot; ${h.branding(c.rhodecode_name)}}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Authentication Plugins')}
14 ${_('Authentication Plugins')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22
22
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27
27
28 <div class='sidebar-col-wrapper'>
28 <div class='sidebar-col-wrapper'>
29
29
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 % for item in resource.get_root().get_nav_list():
32 % for item in resource.get_root().get_nav_list(sort=False):
33 <li ${'class=active' if item == resource else ''}>
33 <li ${'class=active' if item == resource else ''}>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
35 </li>
35 </li>
36 % endfor
36 % endfor
37 </ul>
37 </ul>
38 </div>
38 </div>
39
39
40 <div class="main-content-full-width">
40 <div class="main-content-full-width">
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'), request=request)}
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'), request=request)}
42 <div class="form">
43
44 <div class="panel panel-default">
42 <div class="panel panel-default">
45
43
46 <div class="panel-heading">
44 <div class="panel-heading">
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
45 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
48 </div>
46 </div>
49
47
50 <div class="fields panel-body">
48 <div class="panel-body">
51
49
52 <div class="field">
50
53 <div class="label">${_("Enabled Plugins")}</div>
51 <div class="label">${_("Ordered Enabled Plugins")}</div>
54 <div class="textarea text-area editor">
52 <div class="textarea text-area editor">
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
53 ${h.textarea('auth_plugins',cols=120,rows=20,class_="medium")}
56 </div>
54 </div>
57 <p class="help-block pre-formatting">${_('List of plugins, separated by commas.'
55 <div class="field">
56 <p class="help-block pre-formatting">${_('List of plugins, separated by commas.'
58 '\nThe order of the plugins is also the order in which '
57 '\nThe order of the plugins is also the order in which '
59 'RhodeCode Enterprise will try to authenticate a user.')}</p>
58 'RhodeCode Enterprise will try to authenticate a user.')}
60 </div>
59 </p>
60 </div>
61
61
62 <div class="field">
62 <table class="rctable">
63 <div class="label">${_('Available Built-in Plugins')}</div>
63 <th>${_('Activate')}</th>
64 <ul class="auth_plugins">
64 <th>${_('Plugin Name')}</th>
65 %for plugin in available_plugins:
65 <th>${_('Documentation')}</th>
66 <li>
66 <th>${_('Plugin ID')}</th>
67 <div class="auth_buttons">
67 %for plugin in available_plugins:
68 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
68 <tr>
69 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
69 <td>
70 </span>
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
71 ${plugin.get_display_name()} (${plugin.get_id()})
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
72 </div>
72 </span>
73 </li>
73 </td>
74 %endfor
74 <td>${plugin.get_display_name()}</td>
75 </ul>
75 <td>
76 </div>
76 % if plugin.docs():
77 <a href="${plugin.docs()}">docs</a>
78 % endif
79 </td>
80 <td>${plugin.get_id()}</td>
81 </tr>
82 %endfor
83 </table>
77
84
78 <div class="buttons">
85 <div class="buttons">
79 ${h.submit('save',_('Save'),class_="btn")}
86 ${h.submit('save',_('Save'),class_="btn")}
80 </div>
87 </div>
81 </div>
88 </div>
82 </div>
89 </div>
83 </div>
84 ${h.end_form()}
90 ${h.end_form()}
85 </div>
91 </div>
86 </div>
92 </div>
87 </div>
93 </div>
88
94
89 <script>
95 <script>
90 $('.toggle-plugin').click(function(e){
96 $('.toggle-plugin').click(function(e){
91 var auth_plugins_input = $('#auth_plugins');
97 var auth_plugins_input = $('#auth_plugins');
92 var elems = [];
98 var elems = [];
93
99
94 $.each(auth_plugins_input.val().split(',') , function (index, element) {
100 $.each(auth_plugins_input.val().split(',') , function (index, element) {
95 if (element !== "") {
101 if (element !== "") {
96 elems.push(element.strip())
102 elems.push(element.strip())
97 }
103 }
98 });
104 });
99
105
100 var cur_button = e.currentTarget;
106 var cur_button = e.currentTarget;
101 var plugin_id = $(cur_button).attr('plugin_id');
107 var plugin_id = $(cur_button).attr('plugin_id');
102 if($(cur_button).hasClass('btn-success')){
108 if($(cur_button).hasClass('btn-success')){
103 elems.splice(elems.indexOf(plugin_id), 1);
109 elems.splice(elems.indexOf(plugin_id), 1);
104 auth_plugins_input.val(elems.join(',\n'));
110 auth_plugins_input.val(elems.join(',\n'));
105 $(cur_button).removeClass('btn-success');
111 $(cur_button).removeClass('btn-success');
106 cur_button.innerHTML = _gettext('disabled');
112 cur_button.innerHTML = _gettext('disabled');
107 }
113 }
108 else{
114 else{
109 if(elems.indexOf(plugin_id) == -1){
115 if(elems.indexOf(plugin_id) == -1){
110 elems.push(plugin_id);
116 elems.push(plugin_id);
111 }
117 }
112 auth_plugins_input.val(elems.join(',\n'));
118 auth_plugins_input.val(elems.join(',\n'));
113 $(cur_button).addClass('btn-success');
119 $(cur_button).addClass('btn-success');
114 cur_button.innerHTML = _gettext('enabled');
120 cur_button.innerHTML = _gettext('enabled');
115 }
121 }
116 });
122 });
117 </script>
123 </script>
118 </%def>
124 </%def>
@@ -1,323 +1,323 b''
1 ${h.secure_form(h.route_path('admin_settings_global_update'), request=request)}
1 ${h.secure_form(h.route_path('admin_settings_global_update'), request=request)}
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading" id="branding-options">
4 <div class="panel-heading" id="branding-options">
5 <h3 class="panel-title">${_('Branding')} <a class="permalink" href="#branding-options"> ΒΆ</a></h3>
5 <h3 class="panel-title">${_('Branding')} <a class="permalink" href="#branding-options"> ΒΆ</a></h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="label">
8 <div class="label">
9 <label for="rhodecode_title">${_('Title')}</label>
9 <label for="rhodecode_title">${_('Title')}</label>
10 </div>
10 </div>
11 <div class="field input">
11 <div class="field input">
12 ${h.text('rhodecode_title',size=60)}
12 ${h.text('rhodecode_title',size=60)}
13 </div>
13 </div>
14 <div class="field">
14 <div class="field">
15 <span class="help-block">
15 <span class="help-block">
16 ${_('Set a custom title for your RhodeCode instance (limited to 40 characters).')}
16 ${_('Set a custom title for your RhodeCode instance (limited to 40 characters).')}
17 </span>
17 </span>
18 </div>
18 </div>
19 <div class="label">
19 <div class="label">
20 <label for="rhodecode_realm">${_('HTTP[S] authentication realm')}</label>
20 <label for="rhodecode_realm">${_('HTTP[S] authentication realm')}</label>
21 </div>
21 </div>
22 <div class="field input">
22 <div class="field input">
23 ${h.text('rhodecode_realm',size=60)}
23 ${h.text('rhodecode_realm',size=60)}
24 </div>
24 </div>
25 <div class="field">
25 <div class="field">
26 <span class="help-block">
26 <span class="help-block">
27 ${_('Set a custom text that is shown as authentication message to clients trying to connect.')}
27 ${_('Set a custom text that is shown as authentication message to clients trying to connect.')}
28 </span>
28 </span>
29 </div>
29 </div>
30 </div>
30 </div>
31 </div>
31 </div>
32
32
33
33
34 <div class="panel panel-default">
34 <div class="panel panel-default">
35 <div class="panel-heading" id="personal-group-options">
35 <div class="panel-heading" id="personal-group-options">
36 <h3 class="panel-title">${_('Personal Repository Group')} <a class="permalink" href="#personal-group-options"> ΒΆ</a></h3>
36 <h3 class="panel-title">${_('Personal Repository Group')} <a class="permalink" href="#personal-group-options"> ΒΆ</a></h3>
37 </div>
37 </div>
38 <div class="panel-body">
38 <div class="panel-body">
39 <div class="checkbox">
39 <div class="checkbox">
40 ${h.checkbox('rhodecode_create_personal_repo_group','True')}
40 ${h.checkbox('rhodecode_create_personal_repo_group','True')}
41 <label for="rhodecode_create_personal_repo_group">${_('Create Personal Repository Group')}</label>
41 <label for="rhodecode_create_personal_repo_group">${_('Create Personal Repository Group')}</label>
42 </div>
42 </div>
43 <span class="help-block">
43 <span class="help-block">
44 ${_('Always create Personal Repository Groups for new users.')} <br/>
44 ${_('Always create Personal Repository Groups for new users.')} <br/>
45 ${_('When creating new users from add user form or API you can still turn this off via a checkbox or flag')}
45 ${_('When creating new users from add user form or API you can still turn this off via a checkbox or flag')}
46 </span>
46 </span>
47
47
48 <div class="label">
48 <div class="label">
49 <label for="rhodecode_personal_repo_group_pattern">${_('Personal Repo Group Pattern')}</label>
49 <label for="rhodecode_personal_repo_group_pattern">${_('Personal Repo Group Pattern')}</label>
50 </div>
50 </div>
51 <div class="field input">
51 <div class="field input">
52 ${h.text('rhodecode_personal_repo_group_pattern',size=60, placeholder=c.personal_repo_group_default_pattern)}
52 ${h.text('rhodecode_personal_repo_group_pattern',size=60, placeholder=c.personal_repo_group_default_pattern)}
53 </div>
53 </div>
54 <span class="help-block">
54 <span class="help-block">
55 ${_('Pattern used to create Personal Repository Groups. Prefix can be other existing repository group path[s], eg. /u/${username}')} <br/>
55 ${_('Pattern used to create Personal Repository Groups. Prefix can be other existing repository group path[s], eg. /u/${username}')} <br/>
56 ${_('Available variables are currently ${username} and ${user_id}')}
56 ${_('Available variables are currently ${username} and ${user_id}')}
57 </span>
57 </span>
58 </div>
58 </div>
59 </div>
59 </div>
60
60
61
61
62 <div class="panel panel-default">
62 <div class="panel panel-default">
63 <div class="panel-heading" id="captcha-options">
63 <div class="panel-heading" id="captcha-options">
64 <h3 class="panel-title">${_('Registration Captcha')} <a class="permalink" href="#captcha-options"> ΒΆ</a></h3>
64 <h3 class="panel-title">${_('Registration Captcha')} <a class="permalink" href="#captcha-options"> ΒΆ</a></h3>
65 </div>
65 </div>
66 <div class="panel-body">
66 <div class="panel-body">
67 <div class="label">
67 <div class="label">
68 <label for="rhodecode_captcha_public_key">${_('Google reCaptcha v2 site key.')}</label>
68 <label for="rhodecode_captcha_public_key">${_('Google reCaptcha v2 site key.')}</label>
69 </div>
69 </div>
70 <div class="field input">
70 <div class="field input">
71 ${h.text('rhodecode_captcha_public_key',size=60)}
71 ${h.text('rhodecode_captcha_public_key',size=60)}
72 </div>
72 </div>
73 <div class="field">
73 <div class="field">
74 <span class="help-block">
74 <span class="help-block">
75 ${_('Site key for reCaptcha v2 system.')}
75 ${_('Site key for reCaptcha v2 system.')}
76 </span>
76 </span>
77 </div>
77 </div>
78
78
79 <div class="label">
79 <div class="label">
80 <label for="rhodecode_captcha_private_key">${_('Google reCaptcha v2 secret key.')}</label>
80 <label for="rhodecode_captcha_private_key">${_('Google reCaptcha v2 secret key.')}</label>
81 </div>
81 </div>
82 <div class="field input">
82 <div class="field input">
83 ${h.text('rhodecode_captcha_private_key',size=60)}
83 ${h.text('rhodecode_captcha_private_key',size=60)}
84 </div>
84 </div>
85 <div class="field">
85 <div class="field">
86 <span class="help-block">
86 <span class="help-block">
87 ${_('Secret key for reCaptcha v2 system. Setting this value will enable captcha on registration and password reset forms.')}
87 ${_('Secret key for reCaptcha v2 system. Setting this value will enable captcha on registration and password reset forms.')}
88 </span>
88 </span>
89 </div>
89 </div>
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="panel panel-default">
93 <div class="panel panel-default">
94 <div class="panel-heading" id="header-code-options">
94 <div class="panel-heading" id="header-code-options">
95 <h3 class="panel-title">${_('Custom Header Code')} <a class="permalink" href="#header-code-options"> ΒΆ</a></h3>
95 <h3 class="panel-title">${_('Custom Header Code')} <a class="permalink" href="#header-code-options"> ΒΆ</a></h3>
96 </div>
96 </div>
97 <div class="panel-body">
97 <div class="panel-body">
98 <div class="select">
98 <div class="select">
99 <select id="pre_template" >
99 <select id="pre_template" >
100 <option value="#">${_('Templates...')}</option>
100 <option value="#">${_('Templates...')}</option>
101 <option value="ga">Google Analytics</option>
101 <option value="ga">Google Analytics</option>
102 <option value="clicky">Clicky</option>
102 <option value="clicky">Clicky</option>
103 <option value="server_announce">${_('Server Announcement')}</option>
103 <option value="server_announce">${_('Server Announcement')}</option>
104 <option value="flash_filtering">${_('Flash message filtering')}</option>
104 <option value="flash_filtering">${_('Flash message filtering')}</option>
105 <option value="custom_logo">${_('Custom logos')}</option>
105 <option value="custom_logo">${_('Custom logos')}</option>
106 </select>
106 </select>
107 </div>
107 </div>
108 <div style="padding: 10px 0px"></div>
108 <div style="padding: 10px 0px"></div>
109 <div class="textarea text-area">
109 <div class="textarea text-area">
110 ${h.textarea('rhodecode_pre_code',cols=23,rows=5,class_="medium")}
110 ${h.textarea('rhodecode_pre_code',cols=23,rows=5,class_="medium")}
111 <span class="help-block">${_('Custom js/css code added at the end of the <header/> tag.')}
111 <span class="help-block">${_('Custom js/css code added at the end of the <header/> tag.')}
112 ${_('Use <script/> or <css/> tags to define custom styling or scripting')}</span>
112 ${_('Use <script/> or <css/> tags to define custom styling or scripting')}</span>
113 </div>
113 </div>
114 </div>
114 </div>
115 </div>
115 </div>
116
116
117 <div class="panel panel-default">
117 <div class="panel panel-default">
118 <div class="panel-heading" id="footer-code-options">
118 <div class="panel-heading" id="footer-code-options">
119 <h3 class="panel-title">${_('Custom Footer Code')} <a class="permalink" href="#footer-code-options"> ΒΆ</a></h3>
119 <h3 class="panel-title">${_('Custom Footer Code')} <a class="permalink" href="#footer-code-options"> ΒΆ</a></h3>
120 </div>
120 </div>
121 <div class="panel-body">
121 <div class="panel-body">
122 <div class="select">
122 <div class="select">
123 <select id="post_template" >
123 <select id="post_template" >
124 <option value="#">${_('Templates...')}</option>
124 <option value="#">${_('Templates...')}</option>
125 <option value="ga">Google Analytics</option>
125 <option value="ga">Google Analytics</option>
126 <option value="clicky">Clicky</option>
126 <option value="clicky">Clicky</option>
127 <option value="server_announce">${_('Server Announcement')}</option>
127 <option value="server_announce">${_('Server Announcement')}</option>
128 </select>
128 </select>
129 </div>
129 </div>
130 <div style="padding: 10px 0px"></div>
130 <div style="padding: 10px 0px"></div>
131 <div class="textarea text-area">
131 <div class="textarea text-area">
132 ${h.textarea('rhodecode_post_code',cols=23,rows=5, class_="medium")}
132 ${h.textarea('rhodecode_post_code',cols=23,rows=5, class_="medium")}
133 <span class="help-block">${_('Custom js/css code added at the end of the <body> tag.')}
133 <span class="help-block">${_('Custom js/css code added at the end of the <body> tag.')}
134 ${_('Use <script> or <css> tags to define custom styling or scripting')}</span>
134 ${_('Use <script> or <css> tags to define custom styling or scripting')}</span>
135 </div>
135 </div>
136 </div>
136 </div>
137 </div>
137 </div>
138
138
139 <div class="buttons">
139 <div class="buttons">
140 ${h.submit('save',_('Save settings'),class_="btn")}
140 ${h.submit('save',_('Save settings'),class_="btn")}
141 ${h.reset('reset',_('Reset'),class_="btn")}
141 ${h.reset('reset',_('Reset'),class_="btn")}
142 </div>
142 </div>
143 ${h.end_form()}
143 ${h.end_form()}
144
144
145
145
146
146
147 ## TEMPLATES ##
147 ## TEMPLATES ##
148 ###############
148 ###############
149
149
150 <script id="ga_tmpl" type="text/x-template">
150 <script id="ga_tmpl" type="text/x-template">
151 <%text filter="h">
151 <%text filter="h">
152 <script>
152 <script>
153 // Google Analytics
153 // Google Analytics
154 // Put your Google Analytics code instead of _GACODE_
154 // Put your Google Analytics code instead of _GACODE_
155 var _gaq_code = '_GACODE_';
155 var _gaq_code = '_GACODE_';
156 var _gaq = _gaq || [];
156 var _gaq = _gaq || [];
157 _gaq.push(['_setAccount', _gaq_code]);
157 _gaq.push(['_setAccount', _gaq_code]);
158 _gaq.push(['_trackPageview']);
158 _gaq.push(['_trackPageview']);
159
159
160 (function() {
160 (function() {
161 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
161 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
162 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
162 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
163 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
163 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
164 })();
164 })();
165
165
166 rhodecode_statechange_callback = function(url, data){
166 rhodecode_statechange_callback = function(url, data){
167 // ANALYTICS callback on html5 history state changed
167 // ANALYTICS callback on html5 history state changed
168 // triggered by file browser, url is the new url,
168 // triggered by file browser, url is the new url,
169 // data is extra info passed from the State object
169 // data is extra info passed from the State object
170 if (typeof window._gaq !== 'undefined') {
170 if (typeof window._gaq !== 'undefined') {
171 _gaq.push(['_trackPageview', url]);
171 _gaq.push(['_trackPageview', url]);
172 }
172 }
173 };
173 };
174 </script>
174 </script>
175 </%text>
175 </%text>
176 </script>
176 </script>
177
177
178
178
179
179
180 <script id="clicky_tmpl" type="text/x-template">
180 <script id="clicky_tmpl" type="text/x-template">
181 <%text filter="h">
181 <%text filter="h">
182 <script src="//static.getclicky.com/js" type="text/javascript"></script>
182 <script src="//static.getclicky.com/js" type="text/javascript"></script>
183 <script type="text/javascript">
183 <script type="text/javascript">
184 // Clicky Analytics - should be used in the footer code section.
184 // Clicky Analytics - should be used in the footer code section.
185 // Put your Clicky code instead of _CLICKYCODE_ here,
185 // Put your Clicky code instead of _CLICKYCODE_ here,
186 // and below in the <img> tag.
186 // and below in the <img> tag.
187 var _cl_code = _CLICKYCODE_;
187 var _cl_code = _CLICKYCODE_;
188 try{clicky.init(_cl_code);}catch(e){}
188 try{clicky.init(_cl_code);}catch(e){}
189
189
190 rhodecode_statechange_callback = function(url, data){
190 rhodecode_statechange_callback = function(url, data){
191 // ANALYTICS callback on html5 history state changed
191 // ANALYTICS callback on html5 history state changed
192 // triggered by file browser, url is the new url,
192 // triggered by file browser, url is the new url,
193 // data is extra info passed from the State object
193 // data is extra info passed from the State object
194 if (typeof window.clicky !== 'undefined') {
194 if (typeof window.clicky !== 'undefined') {
195 clicky.log(url);
195 clicky.log(url);
196 }
196 }
197 }
197 }
198 </script>
198 </script>
199 <noscript>
199 <noscript>
200 // Put your clicky code in the src file.
200 // Put your clicky code in the src file.
201 <p><img alt="Clicky" width="1" height="1"
201 <p><img alt="Clicky" width="1" height="1"
202 src="//in.getclicky.com/_CLICKYCODE_ns.gif" /></p>
202 src="//in.getclicky.com/_CLICKYCODE_ns.gif" /></p>
203 </noscript>
203 </noscript>
204 </%text>
204 </%text>
205 </script>
205 </script>
206
206
207
207
208
208
209 <script id="server_announce_tmpl" type='text/x-template'>
209 <script id="server_announce_tmpl" type='text/x-template'>
210 <%text filter="h">
210 <%text filter="h">
211 <script>
211 <script>
212 // Server announcement displayed on the top of the page.
212 // Server announcement displayed on the top of the page.
213 // This can be used to send a global maintenance messages or other
213 // This can be used to send a global maintenance messages or other
214 // important messages to all users of the RhodeCode Enterprise system.
214 // important messages to all users of the RhodeCode Enterprise system.
215
215
216 $(document).ready(function(e) {
216 $(document).ready(function(e) {
217
217
218 // EDIT - put your message below
218 // EDIT - put your message below
219 var message = "TYPE YOUR MESSAGE HERE";
219 var message = "TYPE YOUR MESSAGE HERE";
220
220
221 // EDIT - choose "info"/"warning"/"error"/"success"/"neutral" as appropriate
221 // EDIT - choose "info"/"warning"/"error"/"success"/"neutral" as appropriate
222 var alert_level = "info";
222 var alert_level = "info";
223
223
224 $("#body").prepend(
224 $("#body").prepend(
225 ("<div id='server-announcement' class='"+alert_level+"'>_MSG_"+"</div>").replace("_MSG_", message)
225 ("<div id='server-announcement' class='"+alert_level+"'>_MSG_"+"</div>").replace("_MSG_", message)
226 )
226 )
227 })
227 })
228 </script>
228 </script>
229 </%text>
229 </%text>
230 </script>
230 </script>
231
231
232 <script id="flash_filtering_tmpl" type='text/x-template'>
232 <script id="flash_filtering_tmpl" type='text/x-template'>
233 <%text filter="h">
233 <%text filter="h">
234 <script>
234 <script>
235 // This filters out some flash messages before they are presented to user
235 // This filters out some flash messages before they are presented to user
236 // based on their contents. Could be used to filter out warnings/errors
236 // based on their contents. Could be used to filter out warnings/errors
237 // of license messages
237 // of license messages
238
238
239 var filteredMessages = [];
239 var filteredMessages = [];
240 for(var i =0; i< alertMessagePayloads.length; i++){
240 for(var i =0; i< alertMessagePayloads.length; i++){
241 if (typeof alertMessagePayloads[i].message.subdata.subtype !== 'undefined' &&
241 if (typeof alertMessagePayloads[i].message.subdata.subtype !== 'undefined' &&
242 alertMessagePayloads[i].message.subdata.subtype.indexOf('rc_license') !== -1){
242 alertMessagePayloads[i].message.subdata.subtype.indexOf('rc_license') !== -1){
243 continue
243 continue
244 }
244 }
245 filteredMessages.push(alertMessagePayloads[i]);
245 filteredMessages.push(alertMessagePayloads[i]);
246 }
246 }
247 alertMessagePayloads = filteredMessages;
247 alertMessagePayloads = filteredMessages;
248 </script>
248 </script>
249 </%text>
249 </%text>
250 </script>
250 </script>
251
251
252
252
253 <script id="custom_logo_tmpl" type='text/x-template'>
253 <script id="custom_logo_tmpl" type='text/x-template'>
254 <%text filter="h">
254 <%text filter="h">
255 <script>
255 <script>
256
256
257 $(document).ready(function(e) {
257 $(document).ready(function(e) {
258 // 1) Set custom logo on login/register pages.
258 // 1) Set custom logo on login/register pages.
259
259
260 // external URL, custom company logo
260 // external URL, custom company logo
261 //$('.sign-in-image').attr("src", "http://server.com/logo_path/custom_logo.png");
261 //$('.sign-in-image').attr("src", "http://server.com/logo_path/custom_logo.png");
262
262
263 // Alternative logo from static folder
263 // Alternative logo from static folder
264 $('.sign-in-image').attr("src", "/_static/rhodecode/images/RhodeCode_Logo_Black.png");
264 $('.sign-in-image').attr("src", "/_static/rhodecode/images/RhodeCode_Logo_Black.png");
265
265
266 // set width/height
266 // option to set width/height, adjust if required to make your image look good.
267 $('.sign-in-image').css({"width": "300px", "height": "345px"});
267 $('.sign-in-image').css({"width": "300px", "height": "345px"});
268
268
269 // 2) Header logo on top bar
269 // 2) Header logo on top bar
270 $('.logo-wrapper').find('img').attr('src', 'http://server.com/logo_path/custom_header_logo.png')
270 $('.logo-wrapper').find('img').attr('src', 'http://server.com/logo_path/custom_header_logo.png')
271
271
272 });
272 });
273 </script>
273 </script>
274 </%text>
274 </%text>
275 </script>
275 </script>
276
276
277
277
278 <script>
278 <script>
279 var pre_cm = initCodeMirror('rhodecode_pre_code', '', false);
279 var pre_cm = initCodeMirror('rhodecode_pre_code', '', false);
280 var pre_old = pre_cm.getValue();
280 var pre_old = pre_cm.getValue();
281
281
282 var post_cm = initCodeMirror('rhodecode_post_code', '', false);
282 var post_cm = initCodeMirror('rhodecode_post_code', '', false);
283 var post_old = post_cm.getValue();
283 var post_old = post_cm.getValue();
284
284
285 var get_data = function(type, old) {
285 var get_data = function(type, old) {
286 var get_tmpl = function(tmpl_name){
286 var get_tmpl = function(tmpl_name){
287 // unescape some stuff
287 // unescape some stuff
288 return htmlEnDeCode.htmlDecode($('#'+tmpl_name+'_tmpl').html());
288 return htmlEnDeCode.htmlDecode($('#'+tmpl_name+'_tmpl').html());
289 };
289 };
290 return {
290 return {
291 '#': old,
291 '#': old,
292 'ga': get_tmpl('ga'),
292 'ga': get_tmpl('ga'),
293 'clicky': get_tmpl('clicky'),
293 'clicky': get_tmpl('clicky'),
294 'server_announce': get_tmpl('server_announce'),
294 'server_announce': get_tmpl('server_announce'),
295 'flash_filtering': get_tmpl('flash_filtering'),
295 'flash_filtering': get_tmpl('flash_filtering'),
296 'custom_logo': get_tmpl('custom_logo')
296 'custom_logo': get_tmpl('custom_logo')
297 }[type]
297 }[type]
298 };
298 };
299
299
300 $('#pre_template').select2({
300 $('#pre_template').select2({
301 containerCssClass: 'drop-menu',
301 containerCssClass: 'drop-menu',
302 dropdownCssClass: 'drop-menu-dropdown',
302 dropdownCssClass: 'drop-menu-dropdown',
303 dropdownAutoWidth: true,
303 dropdownAutoWidth: true,
304 minimumResultsForSearch: -1
304 minimumResultsForSearch: -1
305 });
305 });
306
306
307 $('#post_template').select2({
307 $('#post_template').select2({
308 containerCssClass: 'drop-menu',
308 containerCssClass: 'drop-menu',
309 dropdownCssClass: 'drop-menu-dropdown',
309 dropdownCssClass: 'drop-menu-dropdown',
310 dropdownAutoWidth: true,
310 dropdownAutoWidth: true,
311 minimumResultsForSearch: -1
311 minimumResultsForSearch: -1
312 });
312 });
313
313
314 $('#post_template').on('change', function(e){
314 $('#post_template').on('change', function(e){
315 var sel = this.value;
315 var sel = this.value;
316 post_cm.setValue(get_data(sel, post_old))
316 post_cm.setValue(get_data(sel, post_old))
317 });
317 });
318
318
319 $('#pre_template').on('change', function(e){
319 $('#pre_template').on('change', function(e){
320 var sel = this.value;
320 var sel = this.value;
321 pre_cm.setValue(get_data(sel, pre_old))
321 pre_cm.setValue(get_data(sel, pre_old))
322 })
322 })
323 </script>
323 </script>
General Comments 0
You need to be logged in to leave comments. Login now