##// END OF EJS Templates
repositories: enabled support for maintenance commands....
marcink -
r1555:9d1f0e57 default
parent child Browse files
Show More
@@ -0,0 +1,33 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 def includeme(config):
23
24 config.add_route(
25 name='repo_maintenance',
26 pattern='/{repo_name:.*?[^/]}/maintenance', repo_route=True)
27
28 config.add_route(
29 name='repo_maintenance_execute',
30 pattern='/{repo_name:.*?[^/]}/maintenance/execute', repo_route=True)
31
32 # Scan module for configuration decorators.
33 config.scan()
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -0,0 +1,70 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24
25 from rhodecode.apps._base import RepoAppView
26 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
27 NotAnonymous)
28 from rhodecode.lib import repo_maintenance
29
30 log = logging.getLogger(__name__)
31
32
33 class RepoMaintenanceView(RepoAppView):
34 def load_default_context(self):
35 c = self._get_local_tmpl_context()
36
37 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
38 c.repo_info = self.db_repo
39
40 self._register_global_c(c)
41 return c
42
43 @LoginRequired()
44 @NotAnonymous()
45 @HasRepoPermissionAnyDecorator('repository.admin')
46 @view_config(
47 route_name='repo_maintenance', request_method='GET',
48 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
49 def repo_maintenance(self):
50 c = self.load_default_context()
51 c.active = 'maintenance'
52 maintenance = repo_maintenance.RepoMaintenance()
53 c.executable_tasks = maintenance.get_tasks_for_repo(self.db_repo)
54 return self._get_template_context(c)
55
56 @LoginRequired()
57 @NotAnonymous()
58 @HasRepoPermissionAnyDecorator('repository.admin')
59 @view_config(
60 route_name='repo_maintenance_execute', request_method='GET',
61 renderer='json', xhr=True)
62 def repo_maintenance_execute(self):
63 c = self.load_default_context()
64 c.active = 'maintenance'
65 _ = self.request.translate
66
67 maintenance = repo_maintenance.RepoMaintenance()
68 executed_types = maintenance.execute(self.db_repo)
69
70 return executed_types
@@ -0,0 +1,109 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import logging
21
22 log = logging.getLogger(__name__)
23
24
25 class MaintenanceTask(object):
26 human_name = 'undefined'
27
28 def __init__(self, db_repo):
29 self.db_repo = db_repo
30
31 def run(self):
32 """Execute task and return task human value"""
33 raise NotImplementedError()
34
35
36 class GitGC(MaintenanceTask):
37 human_name = 'GIT Garbage collect'
38
39 def _count_objects(self, repo):
40 stdout, stderr = repo.run_git_command(
41 ['count-objects', '-v'], fail_on_stderr=False)
42
43 errors = ' '
44 objects = ' '.join(stdout.splitlines())
45
46 if stderr:
47 errors = '\nSTD ERR:' + '\n'.join(stderr.splitlines())
48 return objects + errors
49
50 def run(self):
51 output = []
52 instance = self.db_repo.scm_instance()
53
54 objects = self._count_objects(instance)
55 output.append(objects)
56 log.debug('GIT objects:%s', objects)
57
58 stdout, stderr = instance.run_git_command(
59 ['gc', '--aggressive'], fail_on_stderr=False)
60
61 out = 'executed git gc --aggressive'
62 if stderr:
63 out = ''.join(stderr.splitlines())
64
65 elif stdout:
66 out = ''.join(stdout.splitlines())
67
68 output.append(out)
69
70 objects = self._count_objects(instance)
71 log.debug('GIT objects:%s', objects)
72 output.append(objects)
73
74 return '\n'.join(output)
75
76
77 class HGVerify(MaintenanceTask):
78 human_name = 'HG Verify repo'
79
80 def run(self):
81 instance = self.db_repo.scm_instance()
82 res = instance.verify()
83 return res
84
85
86 class RepoMaintenance(object):
87 """
88 Performs maintenance of repository based on it's type
89 """
90 tasks = {
91 'hg': [HGVerify],
92 'git': [GitGC],
93 'svn': [],
94 }
95
96 def get_tasks_for_repo(self, db_repo):
97 """
98 fetches human names of tasks pending for execution for given type of repo
99 """
100 tasks = []
101 for task in self.tasks[db_repo.repo_type]:
102 tasks.append(task.human_name)
103 return tasks
104
105 def execute(self, db_repo):
106 executed_tasks = []
107 for task in self.tasks[db_repo.repo_type]:
108 executed_tasks.append(task(db_repo).run())
109 return executed_tasks
@@ -0,0 +1,58 b''
1 <div class="panel panel-default">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Maintenance')}</h3>
4 </div>
5 <div class="panel-body">
6
7 <p>
8 % if c.executable_tasks:
9 ${_('Perform maintenance tasks for this repo, following tasks will be performed')}:
10 <ol>
11 % for task in c.executable_tasks:
12 <li>${task}</li>
13 % endfor
14 </ol>
15 % else:
16 ${_('No maintenance tasks for this repo available')}
17 % endif
18 </p>
19
20 <div id="results" style="display:none; padding: 10px 0px;"></div>
21
22 % if c.executable_tasks:
23 <div class="form">
24 <div class="fields">
25 <button class="btn btn-small btn-primary" onclick="executeTask();return false">
26 ${_('Run Maintenance')}
27 </button>
28 </div>
29 </div>
30 % endif
31
32 </div>
33 </div>
34
35
36 <script>
37
38 executeTask = function() {
39 var btn = $(this);
40 $('#results').show();
41 $('#results').html('<h4>${_('Performing Maintenance')}...</h4>');
42
43 btn.attr('disabled', 'disabled');
44 btn.addClass('disabled');
45
46 var url = "${h.route_path('repo_maintenance_execute', repo_name=c.repo_info.repo_name)}";
47 var success = function (data) {
48 var displayHtml = $('<pre></pre>');
49
50 $(displayHtml).append(data);
51 $('#results').html(displayHtml);
52 btn.removeAttr('disabled');
53 btn.removeClass('disabled');
54 };
55 ajaxGET(url, success, null);
56
57 }
58 </script>
@@ -1,506 +1,507 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 from collections import OrderedDict
25 from collections import OrderedDict
26
26
27 from paste.registry import RegistryManager
27 from paste.registry import RegistryManager
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 from pylons.wsgiapp import PylonsApp
29 from pylons.wsgiapp import PylonsApp
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 from pyramid.events import ApplicationCreated
36 from pyramid.events import ApplicationCreated
37 from pyramid.renderers import render_to_response
37 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42 from rhodecode.model import meta
42 from rhodecode.model import meta
43 from rhodecode.config import patches
43 from rhodecode.config import patches
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.middleware import csrf
47 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.error_handling import (
49 from rhodecode.lib.middleware.error_handling import (
50 PylonsErrorHandlingMiddleware)
50 PylonsErrorHandlingMiddleware)
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
55 from rhodecode.subscribers import (
55 from rhodecode.subscribers import (
56 scan_repositories_if_enabled, write_metadata_if_needed,
56 scan_repositories_if_enabled, write_metadata_if_needed,
57 write_js_routes_if_enabled)
57 write_js_routes_if_enabled)
58
58
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
64 # for certain routes which won't go to pylons to - eg. static files, debugger
64 # for certain routes which won't go to pylons to - eg. static files, debugger
65 # it is only needed for the pylons migration and can be removed once complete
65 # it is only needed for the pylons migration and can be removed once complete
66 class SkippableRoutesMiddleware(RoutesMiddleware):
66 class SkippableRoutesMiddleware(RoutesMiddleware):
67 """ Routes middleware that allows you to skip prefixes """
67 """ Routes middleware that allows you to skip prefixes """
68
68
69 def __init__(self, *args, **kw):
69 def __init__(self, *args, **kw):
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
72
72
73 def __call__(self, environ, start_response):
73 def __call__(self, environ, start_response):
74 for prefix in self.skip_prefixes:
74 for prefix in self.skip_prefixes:
75 if environ['PATH_INFO'].startswith(prefix):
75 if environ['PATH_INFO'].startswith(prefix):
76 # added to avoid the case when a missing /_static route falls
76 # added to avoid the case when a missing /_static route falls
77 # through to pylons and causes an exception as pylons is
77 # through to pylons and causes an exception as pylons is
78 # expecting wsgiorg.routingargs to be set in the environ
78 # expecting wsgiorg.routingargs to be set in the environ
79 # by RoutesMiddleware.
79 # by RoutesMiddleware.
80 if 'wsgiorg.routing_args' not in environ:
80 if 'wsgiorg.routing_args' not in environ:
81 environ['wsgiorg.routing_args'] = (None, {})
81 environ['wsgiorg.routing_args'] = (None, {})
82 return self.app(environ, start_response)
82 return self.app(environ, start_response)
83
83
84 return super(SkippableRoutesMiddleware, self).__call__(
84 return super(SkippableRoutesMiddleware, self).__call__(
85 environ, start_response)
85 environ, start_response)
86
86
87
87
88 def make_app(global_conf, static_files=True, **app_conf):
88 def make_app(global_conf, static_files=True, **app_conf):
89 """Create a Pylons WSGI application and return it
89 """Create a Pylons WSGI application and return it
90
90
91 ``global_conf``
91 ``global_conf``
92 The inherited configuration for this application. Normally from
92 The inherited configuration for this application. Normally from
93 the [DEFAULT] section of the Paste ini file.
93 the [DEFAULT] section of the Paste ini file.
94
94
95 ``app_conf``
95 ``app_conf``
96 The application's local configuration. Normally specified in
96 The application's local configuration. Normally specified in
97 the [app:<name>] section of the Paste ini file (where <name>
97 the [app:<name>] section of the Paste ini file (where <name>
98 defaults to main).
98 defaults to main).
99
99
100 """
100 """
101 # Apply compatibility patches
101 # Apply compatibility patches
102 patches.kombu_1_5_1_python_2_7_11()
102 patches.kombu_1_5_1_python_2_7_11()
103 patches.inspect_getargspec()
103 patches.inspect_getargspec()
104
104
105 # Configure the Pylons environment
105 # Configure the Pylons environment
106 config = load_environment(global_conf, app_conf)
106 config = load_environment(global_conf, app_conf)
107
107
108 # The Pylons WSGI app
108 # The Pylons WSGI app
109 app = PylonsApp(config=config)
109 app = PylonsApp(config=config)
110 if rhodecode.is_test:
110 if rhodecode.is_test:
111 app = csrf.CSRFDetector(app)
111 app = csrf.CSRFDetector(app)
112
112
113 expected_origin = config.get('expected_origin')
113 expected_origin = config.get('expected_origin')
114 if expected_origin:
114 if expected_origin:
115 # The API can be accessed from other Origins.
115 # The API can be accessed from other Origins.
116 app = csrf.OriginChecker(app, expected_origin,
116 app = csrf.OriginChecker(app, expected_origin,
117 skip_urls=[routes.util.url_for('api')])
117 skip_urls=[routes.util.url_for('api')])
118
118
119 # Establish the Registry for this application
119 # Establish the Registry for this application
120 app = RegistryManager(app)
120 app = RegistryManager(app)
121
121
122 app.config = config
122 app.config = config
123
123
124 return app
124 return app
125
125
126
126
127 def make_pyramid_app(global_config, **settings):
127 def make_pyramid_app(global_config, **settings):
128 """
128 """
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
130 application.
130 application.
131
131
132 Specials:
132 Specials:
133
133
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
135 frameworks functional. This involves moving some WSGI middlewares around
135 frameworks functional. This involves moving some WSGI middlewares around
136 and providing access to some data internals, so that the old code is
136 and providing access to some data internals, so that the old code is
137 still functional.
137 still functional.
138
138
139 * The application can also be integrated like a plugin via the call to
139 * The application can also be integrated like a plugin via the call to
140 `includeme`. This is accompanied with the other utility functions which
140 `includeme`. This is accompanied with the other utility functions which
141 are called. Changing this should be done with great care to not break
141 are called. Changing this should be done with great care to not break
142 cases when these fragments are assembled from another place.
142 cases when these fragments are assembled from another place.
143
143
144 """
144 """
145 # The edition string should be available in pylons too, so we add it here
145 # The edition string should be available in pylons too, so we add it here
146 # before copying the settings.
146 # before copying the settings.
147 settings.setdefault('rhodecode.edition', 'Community Edition')
147 settings.setdefault('rhodecode.edition', 'Community Edition')
148
148
149 # As long as our Pylons application does expect "unprepared" settings, make
149 # As long as our Pylons application does expect "unprepared" settings, make
150 # sure that we keep an unmodified copy. This avoids unintentional change of
150 # sure that we keep an unmodified copy. This avoids unintentional change of
151 # behavior in the old application.
151 # behavior in the old application.
152 settings_pylons = settings.copy()
152 settings_pylons = settings.copy()
153
153
154 sanitize_settings_and_apply_defaults(settings)
154 sanitize_settings_and_apply_defaults(settings)
155 config = Configurator(settings=settings)
155 config = Configurator(settings=settings)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
157
157
158 load_pyramid_environment(global_config, settings)
158 load_pyramid_environment(global_config, settings)
159
159
160 includeme_first(config)
160 includeme_first(config)
161 includeme(config)
161 includeme(config)
162 pyramid_app = config.make_wsgi_app()
162 pyramid_app = config.make_wsgi_app()
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
164 pyramid_app.config = config
164 pyramid_app.config = config
165
165
166 # creating the app uses a connection - return it after we are done
166 # creating the app uses a connection - return it after we are done
167 meta.Session.remove()
167 meta.Session.remove()
168
168
169 return pyramid_app
169 return pyramid_app
170
170
171
171
172 def make_not_found_view(config):
172 def make_not_found_view(config):
173 """
173 """
174 This creates the view which should be registered as not-found-view to
174 This creates the view which should be registered as not-found-view to
175 pyramid. Basically it contains of the old pylons app, converted to a view.
175 pyramid. Basically it contains of the old pylons app, converted to a view.
176 Additionally it is wrapped by some other middlewares.
176 Additionally it is wrapped by some other middlewares.
177 """
177 """
178 settings = config.registry.settings
178 settings = config.registry.settings
179 vcs_server_enabled = settings['vcs.server.enable']
179 vcs_server_enabled = settings['vcs.server.enable']
180
180
181 # Make pylons app from unprepared settings.
181 # Make pylons app from unprepared settings.
182 pylons_app = make_app(
182 pylons_app = make_app(
183 config.registry._pylons_compat_global_config,
183 config.registry._pylons_compat_global_config,
184 **config.registry._pylons_compat_settings)
184 **config.registry._pylons_compat_settings)
185 config.registry._pylons_compat_config = pylons_app.config
185 config.registry._pylons_compat_config = pylons_app.config
186
186
187 # Appenlight monitoring.
187 # Appenlight monitoring.
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
189 pylons_app, settings)
189 pylons_app, settings)
190
190
191 # The pylons app is executed inside of the pyramid 404 exception handler.
191 # The pylons app is executed inside of the pyramid 404 exception handler.
192 # Exceptions which are raised inside of it are not handled by pyramid
192 # Exceptions which are raised inside of it are not handled by pyramid
193 # again. Therefore we add a middleware that invokes the error handler in
193 # again. Therefore we add a middleware that invokes the error handler in
194 # case of an exception or error response. This way we return proper error
194 # case of an exception or error response. This way we return proper error
195 # HTML pages in case of an error.
195 # HTML pages in case of an error.
196 reraise = (settings.get('debugtoolbar.enabled', False) or
196 reraise = (settings.get('debugtoolbar.enabled', False) or
197 rhodecode.disable_error_handler)
197 rhodecode.disable_error_handler)
198 pylons_app = PylonsErrorHandlingMiddleware(
198 pylons_app = PylonsErrorHandlingMiddleware(
199 pylons_app, error_handler, reraise)
199 pylons_app, error_handler, reraise)
200
200
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
202 # view to handle the request. Therefore it is wrapped around the pylons
202 # view to handle the request. Therefore it is wrapped around the pylons
203 # app. It has to be outside of the error handling otherwise error responses
203 # app. It has to be outside of the error handling otherwise error responses
204 # from the vcsserver are converted to HTML error pages. This confuses the
204 # from the vcsserver are converted to HTML error pages. This confuses the
205 # command line tools and the user won't get a meaningful error message.
205 # command line tools and the user won't get a meaningful error message.
206 if vcs_server_enabled:
206 if vcs_server_enabled:
207 pylons_app = VCSMiddleware(
207 pylons_app = VCSMiddleware(
208 pylons_app, settings, appenlight_client, registry=config.registry)
208 pylons_app, settings, appenlight_client, registry=config.registry)
209
209
210 # Convert WSGI app to pyramid view and return it.
210 # Convert WSGI app to pyramid view and return it.
211 return wsgiapp(pylons_app)
211 return wsgiapp(pylons_app)
212
212
213
213
214 def add_pylons_compat_data(registry, global_config, settings):
214 def add_pylons_compat_data(registry, global_config, settings):
215 """
215 """
216 Attach data to the registry to support the Pylons integration.
216 Attach data to the registry to support the Pylons integration.
217 """
217 """
218 registry._pylons_compat_global_config = global_config
218 registry._pylons_compat_global_config = global_config
219 registry._pylons_compat_settings = settings
219 registry._pylons_compat_settings = settings
220
220
221
221
222 def error_handler(exception, request):
222 def error_handler(exception, request):
223 import rhodecode
223 import rhodecode
224 from rhodecode.lib.utils2 import AttributeDict
224 from rhodecode.lib.utils2 import AttributeDict
225
225
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
227
227
228 base_response = HTTPInternalServerError()
228 base_response = HTTPInternalServerError()
229 # prefer original exception for the response since it may have headers set
229 # prefer original exception for the response since it may have headers set
230 if isinstance(exception, HTTPException):
230 if isinstance(exception, HTTPException):
231 base_response = exception
231 base_response = exception
232
232
233 def is_http_error(response):
233 def is_http_error(response):
234 # error which should have traceback
234 # error which should have traceback
235 return response.status_code > 499
235 return response.status_code > 499
236
236
237 if is_http_error(base_response):
237 if is_http_error(base_response):
238 log.exception(
238 log.exception(
239 'error occurred handling this request for path: %s', request.path)
239 'error occurred handling this request for path: %s', request.path)
240
240
241 c = AttributeDict()
241 c = AttributeDict()
242 c.error_message = base_response.status
242 c.error_message = base_response.status
243 c.error_explanation = base_response.explanation or str(base_response)
243 c.error_explanation = base_response.explanation or str(base_response)
244 c.visual = AttributeDict()
244 c.visual = AttributeDict()
245
245
246 c.visual.rhodecode_support_url = (
246 c.visual.rhodecode_support_url = (
247 request.registry.settings.get('rhodecode_support_url') or
247 request.registry.settings.get('rhodecode_support_url') or
248 request.route_url('rhodecode_support')
248 request.route_url('rhodecode_support')
249 )
249 )
250 c.redirect_time = 0
250 c.redirect_time = 0
251 c.rhodecode_name = rhodecode_title
251 c.rhodecode_name = rhodecode_title
252 if not c.rhodecode_name:
252 if not c.rhodecode_name:
253 c.rhodecode_name = 'Rhodecode'
253 c.rhodecode_name = 'Rhodecode'
254
254
255 c.causes = []
255 c.causes = []
256 if hasattr(base_response, 'causes'):
256 if hasattr(base_response, 'causes'):
257 c.causes = base_response.causes
257 c.causes = base_response.causes
258
258
259 response = render_to_response(
259 response = render_to_response(
260 '/errors/error_document.mako', {'c': c}, request=request,
260 '/errors/error_document.mako', {'c': c}, request=request,
261 response=base_response)
261 response=base_response)
262
262
263 return response
263 return response
264
264
265
265
266 def includeme(config):
266 def includeme(config):
267 settings = config.registry.settings
267 settings = config.registry.settings
268
268
269 # plugin information
269 # plugin information
270 config.registry.rhodecode_plugins = OrderedDict()
270 config.registry.rhodecode_plugins = OrderedDict()
271
271
272 config.add_directive(
272 config.add_directive(
273 'register_rhodecode_plugin', register_rhodecode_plugin)
273 'register_rhodecode_plugin', register_rhodecode_plugin)
274
274
275 if asbool(settings.get('appenlight', 'false')):
275 if asbool(settings.get('appenlight', 'false')):
276 config.include('appenlight_client.ext.pyramid_tween')
276 config.include('appenlight_client.ext.pyramid_tween')
277
277
278 # Includes which are required. The application would fail without them.
278 # Includes which are required. The application would fail without them.
279 config.include('pyramid_mako')
279 config.include('pyramid_mako')
280 config.include('pyramid_beaker')
280 config.include('pyramid_beaker')
281
281
282 config.include('rhodecode.authentication')
282 config.include('rhodecode.authentication')
283 config.include('rhodecode.integrations')
283 config.include('rhodecode.integrations')
284
284
285 # apps
285 # apps
286 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps._base')
287
287
288 config.include('rhodecode.apps.admin')
288 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.channelstream')
289 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.login')
290 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.repository')
291 config.include('rhodecode.apps.user_profile')
292 config.include('rhodecode.apps.user_profile')
292 config.include('rhodecode.apps.my_account')
293 config.include('rhodecode.apps.my_account')
293 config.include('rhodecode.apps.svn_support')
294 config.include('rhodecode.apps.svn_support')
294
295
295 config.include('rhodecode.tweens')
296 config.include('rhodecode.tweens')
296 config.include('rhodecode.api')
297 config.include('rhodecode.api')
297
298
298 config.add_route(
299 config.add_route(
299 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
300 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
300
301
301 config.add_translation_dirs('rhodecode:i18n/')
302 config.add_translation_dirs('rhodecode:i18n/')
302 settings['default_locale_name'] = settings.get('lang', 'en')
303 settings['default_locale_name'] = settings.get('lang', 'en')
303
304
304 # Add subscribers.
305 # Add subscribers.
305 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
306 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
306 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
307 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
307 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
308 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
308
309
309 # Set the authorization policy.
310 # Set the authorization policy.
310 authz_policy = ACLAuthorizationPolicy()
311 authz_policy = ACLAuthorizationPolicy()
311 config.set_authorization_policy(authz_policy)
312 config.set_authorization_policy(authz_policy)
312
313
313 # Set the default renderer for HTML templates to mako.
314 # Set the default renderer for HTML templates to mako.
314 config.add_mako_renderer('.html')
315 config.add_mako_renderer('.html')
315
316
316 # include RhodeCode plugins
317 # include RhodeCode plugins
317 includes = aslist(settings.get('rhodecode.includes', []))
318 includes = aslist(settings.get('rhodecode.includes', []))
318 for inc in includes:
319 for inc in includes:
319 config.include(inc)
320 config.include(inc)
320
321
321 # This is the glue which allows us to migrate in chunks. By registering the
322 # This is the glue which allows us to migrate in chunks. By registering the
322 # pylons based application as the "Not Found" view in Pyramid, we will
323 # pylons based application as the "Not Found" view in Pyramid, we will
323 # fallback to the old application each time the new one does not yet know
324 # fallback to the old application each time the new one does not yet know
324 # how to handle a request.
325 # how to handle a request.
325 config.add_notfound_view(make_not_found_view(config))
326 config.add_notfound_view(make_not_found_view(config))
326
327
327 if not settings.get('debugtoolbar.enabled', False):
328 if not settings.get('debugtoolbar.enabled', False):
328 # if no toolbar, then any exception gets caught and rendered
329 # if no toolbar, then any exception gets caught and rendered
329 config.add_view(error_handler, context=Exception)
330 config.add_view(error_handler, context=Exception)
330
331
331 config.add_view(error_handler, context=HTTPError)
332 config.add_view(error_handler, context=HTTPError)
332
333
333
334
334 def includeme_first(config):
335 def includeme_first(config):
335 # redirect automatic browser favicon.ico requests to correct place
336 # redirect automatic browser favicon.ico requests to correct place
336 def favicon_redirect(context, request):
337 def favicon_redirect(context, request):
337 return HTTPFound(
338 return HTTPFound(
338 request.static_path('rhodecode:public/images/favicon.ico'))
339 request.static_path('rhodecode:public/images/favicon.ico'))
339
340
340 config.add_view(favicon_redirect, route_name='favicon')
341 config.add_view(favicon_redirect, route_name='favicon')
341 config.add_route('favicon', '/favicon.ico')
342 config.add_route('favicon', '/favicon.ico')
342
343
343 def robots_redirect(context, request):
344 def robots_redirect(context, request):
344 return HTTPFound(
345 return HTTPFound(
345 request.static_path('rhodecode:public/robots.txt'))
346 request.static_path('rhodecode:public/robots.txt'))
346
347
347 config.add_view(robots_redirect, route_name='robots')
348 config.add_view(robots_redirect, route_name='robots')
348 config.add_route('robots', '/robots.txt')
349 config.add_route('robots', '/robots.txt')
349
350
350 config.add_static_view(
351 config.add_static_view(
351 '_static/deform', 'deform:static')
352 '_static/deform', 'deform:static')
352 config.add_static_view(
353 config.add_static_view(
353 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
354 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
354
355
355
356
356 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
357 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
357 """
358 """
358 Apply outer WSGI middlewares around the application.
359 Apply outer WSGI middlewares around the application.
359
360
360 Part of this has been moved up from the Pylons layer, so that the
361 Part of this has been moved up from the Pylons layer, so that the
361 data is also available if old Pylons code is hit through an already ported
362 data is also available if old Pylons code is hit through an already ported
362 view.
363 view.
363 """
364 """
364 settings = config.registry.settings
365 settings = config.registry.settings
365
366
366 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
367 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
367 pyramid_app = HttpsFixup(pyramid_app, settings)
368 pyramid_app = HttpsFixup(pyramid_app, settings)
368
369
369 # Add RoutesMiddleware to support the pylons compatibility tween during
370 # Add RoutesMiddleware to support the pylons compatibility tween during
370 # migration to pyramid.
371 # migration to pyramid.
371 pyramid_app = SkippableRoutesMiddleware(
372 pyramid_app = SkippableRoutesMiddleware(
372 pyramid_app, config.registry._pylons_compat_config['routes.map'],
373 pyramid_app, config.registry._pylons_compat_config['routes.map'],
373 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
374 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
374
375
375 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
376 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
376
377
377 if settings['gzip_responses']:
378 if settings['gzip_responses']:
378 pyramid_app = make_gzip_middleware(
379 pyramid_app = make_gzip_middleware(
379 pyramid_app, settings, compress_level=1)
380 pyramid_app, settings, compress_level=1)
380
381
381 # this should be the outer most middleware in the wsgi stack since
382 # this should be the outer most middleware in the wsgi stack since
382 # middleware like Routes make database calls
383 # middleware like Routes make database calls
383 def pyramid_app_with_cleanup(environ, start_response):
384 def pyramid_app_with_cleanup(environ, start_response):
384 try:
385 try:
385 return pyramid_app(environ, start_response)
386 return pyramid_app(environ, start_response)
386 finally:
387 finally:
387 # Dispose current database session and rollback uncommitted
388 # Dispose current database session and rollback uncommitted
388 # transactions.
389 # transactions.
389 meta.Session.remove()
390 meta.Session.remove()
390
391
391 # In a single threaded mode server, on non sqlite db we should have
392 # In a single threaded mode server, on non sqlite db we should have
392 # '0 Current Checked out connections' at the end of a request,
393 # '0 Current Checked out connections' at the end of a request,
393 # if not, then something, somewhere is leaving a connection open
394 # if not, then something, somewhere is leaving a connection open
394 pool = meta.Base.metadata.bind.engine.pool
395 pool = meta.Base.metadata.bind.engine.pool
395 log.debug('sa pool status: %s', pool.status())
396 log.debug('sa pool status: %s', pool.status())
396
397
397
398
398 return pyramid_app_with_cleanup
399 return pyramid_app_with_cleanup
399
400
400
401
401 def sanitize_settings_and_apply_defaults(settings):
402 def sanitize_settings_and_apply_defaults(settings):
402 """
403 """
403 Applies settings defaults and does all type conversion.
404 Applies settings defaults and does all type conversion.
404
405
405 We would move all settings parsing and preparation into this place, so that
406 We would move all settings parsing and preparation into this place, so that
406 we have only one place left which deals with this part. The remaining parts
407 we have only one place left which deals with this part. The remaining parts
407 of the application would start to rely fully on well prepared settings.
408 of the application would start to rely fully on well prepared settings.
408
409
409 This piece would later be split up per topic to avoid a big fat monster
410 This piece would later be split up per topic to avoid a big fat monster
410 function.
411 function.
411 """
412 """
412
413
413 # Pyramid's mako renderer has to search in the templates folder so that the
414 # Pyramid's mako renderer has to search in the templates folder so that the
414 # old templates still work. Ported and new templates are expected to use
415 # old templates still work. Ported and new templates are expected to use
415 # real asset specifications for the includes.
416 # real asset specifications for the includes.
416 mako_directories = settings.setdefault('mako.directories', [
417 mako_directories = settings.setdefault('mako.directories', [
417 # Base templates of the original Pylons application
418 # Base templates of the original Pylons application
418 'rhodecode:templates',
419 'rhodecode:templates',
419 ])
420 ])
420 log.debug(
421 log.debug(
421 "Using the following Mako template directories: %s",
422 "Using the following Mako template directories: %s",
422 mako_directories)
423 mako_directories)
423
424
424 # Default includes, possible to change as a user
425 # Default includes, possible to change as a user
425 pyramid_includes = settings.setdefault('pyramid.includes', [
426 pyramid_includes = settings.setdefault('pyramid.includes', [
426 'rhodecode.lib.middleware.request_wrapper',
427 'rhodecode.lib.middleware.request_wrapper',
427 ])
428 ])
428 log.debug(
429 log.debug(
429 "Using the following pyramid.includes: %s",
430 "Using the following pyramid.includes: %s",
430 pyramid_includes)
431 pyramid_includes)
431
432
432 # TODO: johbo: Re-think this, usually the call to config.include
433 # TODO: johbo: Re-think this, usually the call to config.include
433 # should allow to pass in a prefix.
434 # should allow to pass in a prefix.
434 settings.setdefault('rhodecode.api.url', '/_admin/api')
435 settings.setdefault('rhodecode.api.url', '/_admin/api')
435
436
436 # Sanitize generic settings.
437 # Sanitize generic settings.
437 _list_setting(settings, 'default_encoding', 'UTF-8')
438 _list_setting(settings, 'default_encoding', 'UTF-8')
438 _bool_setting(settings, 'is_test', 'false')
439 _bool_setting(settings, 'is_test', 'false')
439 _bool_setting(settings, 'gzip_responses', 'false')
440 _bool_setting(settings, 'gzip_responses', 'false')
440
441
441 # Call split out functions that sanitize settings for each topic.
442 # Call split out functions that sanitize settings for each topic.
442 _sanitize_appenlight_settings(settings)
443 _sanitize_appenlight_settings(settings)
443 _sanitize_vcs_settings(settings)
444 _sanitize_vcs_settings(settings)
444
445
445 return settings
446 return settings
446
447
447
448
448 def _sanitize_appenlight_settings(settings):
449 def _sanitize_appenlight_settings(settings):
449 _bool_setting(settings, 'appenlight', 'false')
450 _bool_setting(settings, 'appenlight', 'false')
450
451
451
452
452 def _sanitize_vcs_settings(settings):
453 def _sanitize_vcs_settings(settings):
453 """
454 """
454 Applies settings defaults and does type conversion for all VCS related
455 Applies settings defaults and does type conversion for all VCS related
455 settings.
456 settings.
456 """
457 """
457 _string_setting(settings, 'vcs.svn.compatible_version', '')
458 _string_setting(settings, 'vcs.svn.compatible_version', '')
458 _string_setting(settings, 'git_rev_filter', '--all')
459 _string_setting(settings, 'git_rev_filter', '--all')
459 _string_setting(settings, 'vcs.hooks.protocol', 'http')
460 _string_setting(settings, 'vcs.hooks.protocol', 'http')
460 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
461 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
461 _string_setting(settings, 'vcs.server', '')
462 _string_setting(settings, 'vcs.server', '')
462 _string_setting(settings, 'vcs.server.log_level', 'debug')
463 _string_setting(settings, 'vcs.server.log_level', 'debug')
463 _string_setting(settings, 'vcs.server.protocol', 'http')
464 _string_setting(settings, 'vcs.server.protocol', 'http')
464 _bool_setting(settings, 'startup.import_repos', 'false')
465 _bool_setting(settings, 'startup.import_repos', 'false')
465 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
466 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
466 _bool_setting(settings, 'vcs.server.enable', 'true')
467 _bool_setting(settings, 'vcs.server.enable', 'true')
467 _bool_setting(settings, 'vcs.start_server', 'false')
468 _bool_setting(settings, 'vcs.start_server', 'false')
468 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
469 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
469 _int_setting(settings, 'vcs.connection_timeout', 3600)
470 _int_setting(settings, 'vcs.connection_timeout', 3600)
470
471
471 # Support legacy values of vcs.scm_app_implementation. Legacy
472 # Support legacy values of vcs.scm_app_implementation. Legacy
472 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
473 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
473 # which is now mapped to 'http'.
474 # which is now mapped to 'http'.
474 scm_app_impl = settings['vcs.scm_app_implementation']
475 scm_app_impl = settings['vcs.scm_app_implementation']
475 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
476 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
476 settings['vcs.scm_app_implementation'] = 'http'
477 settings['vcs.scm_app_implementation'] = 'http'
477
478
478
479
479 def _int_setting(settings, name, default):
480 def _int_setting(settings, name, default):
480 settings[name] = int(settings.get(name, default))
481 settings[name] = int(settings.get(name, default))
481
482
482
483
483 def _bool_setting(settings, name, default):
484 def _bool_setting(settings, name, default):
484 input = settings.get(name, default)
485 input = settings.get(name, default)
485 if isinstance(input, unicode):
486 if isinstance(input, unicode):
486 input = input.encode('utf8')
487 input = input.encode('utf8')
487 settings[name] = asbool(input)
488 settings[name] = asbool(input)
488
489
489
490
490 def _list_setting(settings, name, default):
491 def _list_setting(settings, name, default):
491 raw_value = settings.get(name, default)
492 raw_value = settings.get(name, default)
492
493
493 old_separator = ','
494 old_separator = ','
494 if old_separator in raw_value:
495 if old_separator in raw_value:
495 # If we get a comma separated list, pass it to our own function.
496 # If we get a comma separated list, pass it to our own function.
496 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
497 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
497 else:
498 else:
498 # Otherwise we assume it uses pyramids space/newline separation.
499 # Otherwise we assume it uses pyramids space/newline separation.
499 settings[name] = aslist(raw_value)
500 settings[name] = aslist(raw_value)
500
501
501
502
502 def _string_setting(settings, name, default, lower=True):
503 def _string_setting(settings, name, default, lower=True):
503 value = settings.get(name, default)
504 value = settings.get(name, default)
504 if lower:
505 if lower:
505 value = value.lower()
506 value = value.lower()
506 settings[name] = value
507 settings[name] = value
@@ -1,100 +1,103 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ##
2 ##
3 ## See also repo_settings.html
3 ## See also repo_settings.html
4 ##
4 ##
5 <%inherit file="/base/base.mako"/>
5 <%inherit file="/base/base.mako"/>
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('%s repository settings') % c.repo_info.repo_name}
8 ${_('%s repository settings') % c.repo_info.repo_name}
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Settings')}
15 ${_('Settings')}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='repositories')}
19 ${self.menu_items(active='repositories')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.repo_menu(active='options')}
23 ${self.repo_menu(active='options')}
24 </%def>
24 </%def>
25
25
26 <%def name="main_content()">
26 <%def name="main_content()">
27 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
27 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
28 </%def>
28 </%def>
29
29
30
30
31 <%def name="main()">
31 <%def name="main()">
32 <div class="box">
32 <div class="box">
33 <div class="title">
33 <div class="title">
34 ${self.repo_page_title(c.rhodecode_db_repo)}
34 ${self.repo_page_title(c.rhodecode_db_repo)}
35 ${self.breadcrumbs()}
35 ${self.breadcrumbs()}
36 </div>
36 </div>
37
37
38 <div class="sidebar-col-wrapper scw-small">
38 <div class="sidebar-col-wrapper scw-small">
39 ##main
39 ##main
40 <div class="sidebar">
40 <div class="sidebar">
41 <ul class="nav nav-pills nav-stacked">
41 <ul class="nav nav-pills nav-stacked">
42 <li class="${'active' if c.active=='settings' else ''}">
42 <li class="${'active' if c.active=='settings' else ''}">
43 <a href="${h.url('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
43 <a href="${h.url('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
44 </li>
44 </li>
45 <li class="${'active' if c.active=='permissions' else ''}">
45 <li class="${'active' if c.active=='permissions' else ''}">
46 <a href="${h.url('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
46 <a href="${h.url('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
47 </li>
47 </li>
48 <li class="${'active' if c.active=='advanced' else ''}">
48 <li class="${'active' if c.active=='advanced' else ''}">
49 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
49 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
50 </li>
50 </li>
51 <li class="${'active' if c.active=='vcs' else ''}">
51 <li class="${'active' if c.active=='vcs' else ''}">
52 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
52 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
53 </li>
53 </li>
54 <li class="${'active' if c.active=='fields' else ''}">
54 <li class="${'active' if c.active=='fields' else ''}">
55 <a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
55 <a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
56 </li>
56 </li>
57 <li class="${'active' if c.active=='issuetracker' else ''}">
57 <li class="${'active' if c.active=='issuetracker' else ''}">
58 <a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
58 <a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
59 </li>
59 </li>
60 <li class="${'active' if c.active=='caches' else ''}">
60 <li class="${'active' if c.active=='caches' else ''}">
61 <a href="${h.url('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
61 <a href="${h.url('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
62 </li>
62 </li>
63 %if c.repo_info.repo_type != 'svn':
63 %if c.repo_info.repo_type != 'svn':
64 <li class="${'active' if c.active=='remote' else ''}">
64 <li class="${'active' if c.active=='remote' else ''}">
65 <a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
65 <a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
66 </li>
66 </li>
67 %endif
67 %endif
68 <li class="${'active' if c.active=='statistics' else ''}">
68 <li class="${'active' if c.active=='statistics' else ''}">
69 <a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
69 <a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
70 </li>
70 </li>
71 <li class="${'active' if c.active=='integrations' else ''}">
71 <li class="${'active' if c.active=='integrations' else ''}">
72 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
72 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
73 </li>
73 </li>
74 <li class="${'active' if c.active=='maintenance' else ''}">
75 <a href="${h.route_path('repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
76 </li>
74 ## TODO: dan: replace repo navigation with navlist registry like with
77 ## TODO: dan: replace repo navigation with navlist registry like with
75 ## admin menu. First must find way to allow runtime configuration
78 ## admin menu. First must find way to allow runtime configuration
76 ## it to account for the c.repo_info.repo_type != 'svn' call above
79 ## it to account for the c.repo_info.repo_type != 'svn' call above
77 <%
80 <%
78 reviewer_settings = False
81 reviewer_settings = False
79 try:
82 try:
80 import rc_reviewers
83 import rc_reviewers
81 reviewer_settings = True
84 reviewer_settings = True
82 except ImportError:
85 except ImportError:
83 pass
86 pass
84 %>
87 %>
85 %if reviewer_settings:
88 %if reviewer_settings:
86 <li class="${'active' if c.active=='reviewers' else ''}">
89 <li class="${'active' if c.active=='reviewers' else ''}">
87 <a href="${h.route_path('repo_reviewers_home', repo_name=c.repo_name)}">${_('Reviewers')}</a>
90 <a href="${h.route_path('repo_reviewers_home', repo_name=c.repo_name)}">${_('Reviewers')}</a>
88 </li>
91 </li>
89 %endif
92 %endif
90 </ul>
93 </ul>
91 </div>
94 </div>
92
95
93 <div class="main-content-full-width">
96 <div class="main-content-full-width">
94 ${self.main_content()}
97 ${self.main_content()}
95 </div>
98 </div>
96
99
97 </div>
100 </div>
98 </div>
101 </div>
99
102
100 </%def> No newline at end of file
103 </%def>
@@ -1,295 +1,295 b''
1 ${h.secure_form(url('admin_settings_global'), method='post')}
1 ${h.secure_form(url('admin_settings_global'), method='post')}
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 public key')}</label>
68 <label for="rhodecode_captcha_public_key">${_('Google ReCaptcha public 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 ${_('Public key for reCaptcha system.')}
75 ${_('Public key for reCaptcha 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 private key')}</label>
80 <label for="rhodecode_captcha_private_key">${_('Google ReCaptcha private 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 ${_('Private key for reCaptcha system. Setting this value will enable captcha on registration')}
87 ${_('Private key for reCaptcha system. Setting this value will enable captcha on registration')}
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 </select>
105 </select>
106 </div>
106 </div>
107 <div style="padding: 10px 0px"></div>
107 <div style="padding: 10px 0px"></div>
108 <div class="textarea text-area">
108 <div class="textarea text-area">
109 ${h.textarea('rhodecode_pre_code',cols=23,rows=5,class_="medium")}
109 ${h.textarea('rhodecode_pre_code',cols=23,rows=5,class_="medium")}
110 <span class="help-block">${_('Custom js/css code added at the end of the <header/> tag.')}
110 <span class="help-block">${_('Custom js/css code added at the end of the <header/> tag.')}
111 ${_('Use <script/> or <css/> tags to define custom styling or scripting')}</span>
111 ${_('Use <script/> or <css/> tags to define custom styling or scripting')}</span>
112 </div>
112 </div>
113 </div>
113 </div>
114 </div>
114 </div>
115
115
116 <div class="panel panel-default">
116 <div class="panel panel-default">
117 <div class="panel-heading" id="footer-code-options">
117 <div class="panel-heading" id="footer-code-options">
118 <h3 class="panel-title">${_('Custom Footer Code')} <a class="permalink" href="#footer-code-options"> ΒΆ</a></h3>
118 <h3 class="panel-title">${_('Custom Footer Code')} <a class="permalink" href="#footer-code-options"> ΒΆ</a></h3>
119 </div>
119 </div>
120 <div class="panel-body">
120 <div class="panel-body">
121 <div class="select">
121 <div class="select">
122 <select id="post_template" >
122 <select id="post_template" >
123 <option value="#">${_('Templates...')}</option>
123 <option value="#">${_('Templates...')}</option>
124 <option value="ga">Google Analytics</option>
124 <option value="ga">Google Analytics</option>
125 <option value="clicky">Clicky</option>
125 <option value="clicky">Clicky</option>
126 <option value="server_announce">${_('Server Announcement')}</option>
126 <option value="server_announce">${_('Server Announcement')}</option>
127 </select>
127 </select>
128 </div>
128 </div>
129 <div style="padding: 10px 0px"></div>
129 <div style="padding: 10px 0px"></div>
130 <div class="textarea text-area">
130 <div class="textarea text-area">
131 ${h.textarea('rhodecode_post_code',cols=23,rows=5, class_="medium")}
131 ${h.textarea('rhodecode_post_code',cols=23,rows=5, class_="medium")}
132 <span class="help-block">${_('Custom js/css code added at the end of the <body> tag.')}
132 <span class="help-block">${_('Custom js/css code added at the end of the <body> tag.')}
133 ${_('Use <script> or <css> tags to define custom styling or scripting')}</span>
133 ${_('Use <script> or <css> tags to define custom styling or scripting')}</span>
134 </div>
134 </div>
135 </div>
135 </div>
136 </div>
136 </div>
137
137
138 <div class="buttons">
138 <div class="buttons">
139 ${h.submit('save',_('Save settings'),class_="btn")}
139 ${h.submit('save',_('Save settings'),class_="btn")}
140 ${h.reset('reset',_('Reset'),class_="btn")}
140 ${h.reset('reset',_('Reset'),class_="btn")}
141 </div>
141 </div>
142 ${h.end_form()}
142 ${h.end_form()}
143
143
144
144
145
145
146 ## TEMPLATES ##
146 ## TEMPLATES ##
147 ###############
147 ###############
148
148
149 <script id="ga_tmpl" type="text/x-template">
149 <script id="ga_tmpl" type="text/x-template">
150 <%text filter="h">
150 <%text filter="h">
151 <script>
151 <script>
152 // Google Analytics
152 // Google Analytics
153 // Put your Google Analytics code instead of _GACODE_
153 // Put your Google Analytics code instead of _GACODE_
154 var _gaq_code = '_GACODE_';
154 var _gaq_code = '_GACODE_';
155 var _gaq = _gaq || [];
155 var _gaq = _gaq || [];
156 _gaq.push(['_setAccount', _gaq_code]);
156 _gaq.push(['_setAccount', _gaq_code]);
157 _gaq.push(['_trackPageview']);
157 _gaq.push(['_trackPageview']);
158
158
159 (function() {
159 (function() {
160 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
160 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
161 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
161 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
162 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
162 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
163 })();
163 })();
164
164
165 rhodecode_statechange_callback = function(url, data){
165 rhodecode_statechange_callback = function(url, data){
166 // ANALYTICS callback on html5 history state changed
166 // ANALYTICS callback on html5 history state changed
167 // triggered by file browser, url is the new url,
167 // triggered by file browser, url is the new url,
168 // data is extra info passed from the State object
168 // data is extra info passed from the State object
169 if (typeof window._gaq !== 'undefined') {
169 if (typeof window._gaq !== 'undefined') {
170 _gaq.push(['_trackPageview', url]);
170 _gaq.push(['_trackPageview', url]);
171 }
171 }
172 };
172 };
173 </script>
173 </script>
174 </%text>
174 </%text>
175 </script>
175 </script>
176
176
177
177
178
178
179 <script id="clicky_tmpl" type="text/x-template">
179 <script id="clicky_tmpl" type="text/x-template">
180 <%text filter="h">
180 <%text filter="h">
181 <script src="//static.getclicky.com/js" type="text/javascript"></script>
181 <script src="//static.getclicky.com/js" type="text/javascript"></script>
182 <script type="text/javascript">
182 <script type="text/javascript">
183 // Clicky Analytics - should be used in the footer code section.
183 // Clicky Analytics - should be used in the footer code section.
184 // Put your Clicky code instead of _CLICKYCODE_ here,
184 // Put your Clicky code instead of _CLICKYCODE_ here,
185 // and below in the <img> tag.
185 // and below in the <img> tag.
186 var _cl_code = _CLICKYCODE_;
186 var _cl_code = _CLICKYCODE_;
187 try{clicky.init(_cl_code);}catch(e){}
187 try{clicky.init(_cl_code);}catch(e){}
188
188
189 rhodecode_statechange_callback = function(url, data){
189 rhodecode_statechange_callback = function(url, data){
190 // ANALYTICS callback on html5 history state changed
190 // ANALYTICS callback on html5 history state changed
191 // triggered by file browser, url is the new url,
191 // triggered by file browser, url is the new url,
192 // data is extra info passed from the State object
192 // data is extra info passed from the State object
193 if (typeof window.clicky !== 'undefined') {
193 if (typeof window.clicky !== 'undefined') {
194 clicky.log(url);
194 clicky.log(url);
195 }
195 }
196 }
196 }
197 </script>
197 </script>
198 <noscript>
198 <noscript>
199 // Put your clicky code in the src file.
199 // Put your clicky code in the src file.
200 <p><img alt="Clicky" width="1" height="1"
200 <p><img alt="Clicky" width="1" height="1"
201 src="//in.getclicky.com/_CLICKYCODE_ns.gif" /></p>
201 src="//in.getclicky.com/_CLICKYCODE_ns.gif" /></p>
202 </noscript>
202 </noscript>
203 </%text>
203 </%text>
204 </script>
204 </script>
205
205
206
206
207
207
208 <script id="server_announce_tmpl" type='text/x-template'>
208 <script id="server_announce_tmpl" type='text/x-template'>
209 <%text filter="h">
209 <%text filter="h">
210 <script>
210 <script>
211 // Server announcement displayed on the top of the page.
211 // Server announcement displayed on the top of the page.
212 // This can be used to send a global maintainance messages or other
212 // This can be used to send a global maintenance messages or other
213 // important messages to all users of the RhodeCode Enterprise system.
213 // important messages to all users of the RhodeCode Enterprise system.
214
214
215 $(document).ready(function(e){
215 $(document).ready(function(e){
216
216
217 // EDIT - put your message below
217 // EDIT - put your message below
218 var message = "TYPE YOUR MESSAGE HERE";
218 var message = "TYPE YOUR MESSAGE HERE";
219
219
220 // EDIT - choose "info"/"warning"/"error"/"success"/"neutral" as appropriate
220 // EDIT - choose "info"/"warning"/"error"/"success"/"neutral" as appropriate
221 var alert_level = "info";
221 var alert_level = "info";
222
222
223 $("#body").prepend(
223 $("#body").prepend(
224 ("<div id='server-announcement' class='"+alert_level+"'>_MSG_"+"</div>").replace("_MSG_", message)
224 ("<div id='server-announcement' class='"+alert_level+"'>_MSG_"+"</div>").replace("_MSG_", message)
225 )
225 )
226 })
226 })
227 </script>
227 </script>
228 </%text>
228 </%text>
229 </script>
229 </script>
230
230
231 <script id="flash_filtering_tmpl" type='text/x-template'>
231 <script id="flash_filtering_tmpl" type='text/x-template'>
232 <%text filter="h">
232 <%text filter="h">
233 <script>
233 <script>
234 // This filters out some flash messages before they are presented to user
234 // This filters out some flash messages before they are presented to user
235 // based on their contents. Could be used to filter out warnings/errors
235 // based on their contents. Could be used to filter out warnings/errors
236 // of license messages
236 // of license messages
237
237
238 var filteredMessages = [];
238 var filteredMessages = [];
239 for(var i =0; i< alertMessagePayloads.length; i++){
239 for(var i =0; i< alertMessagePayloads.length; i++){
240 if (typeof alertMessagePayloads[i].message.subdata.subtype !== 'undefined' &&
240 if (typeof alertMessagePayloads[i].message.subdata.subtype !== 'undefined' &&
241 alertMessagePayloads[i].message.subdata.subtype.indexOf('rc_license') !== -1){
241 alertMessagePayloads[i].message.subdata.subtype.indexOf('rc_license') !== -1){
242 continue
242 continue
243 }
243 }
244 filteredMessages.push(alertMessagePayloads[i]);
244 filteredMessages.push(alertMessagePayloads[i]);
245 }
245 }
246 alertMessagePayloads = filteredMessages;
246 alertMessagePayloads = filteredMessages;
247 </script>
247 </script>
248 </%text>
248 </%text>
249 </script>
249 </script>
250
250
251 <script>
251 <script>
252 var pre_cm = initCodeMirror('rhodecode_pre_code', '', false);
252 var pre_cm = initCodeMirror('rhodecode_pre_code', '', false);
253 var pre_old = pre_cm.getValue();
253 var pre_old = pre_cm.getValue();
254
254
255 var post_cm = initCodeMirror('rhodecode_post_code', '', false);
255 var post_cm = initCodeMirror('rhodecode_post_code', '', false);
256 var post_old = post_cm.getValue();
256 var post_old = post_cm.getValue();
257
257
258 var get_data = function(type, old){
258 var get_data = function(type, old){
259 var get_tmpl = function(tmpl_name){
259 var get_tmpl = function(tmpl_name){
260 // unescape some stuff
260 // unescape some stuff
261 return htmlEnDeCode.htmlDecode($('#'+tmpl_name+'_tmpl').html());
261 return htmlEnDeCode.htmlDecode($('#'+tmpl_name+'_tmpl').html());
262 };
262 };
263 return {
263 return {
264 '#': old,
264 '#': old,
265 'ga': get_tmpl('ga'),
265 'ga': get_tmpl('ga'),
266 'clicky': get_tmpl('clicky'),
266 'clicky': get_tmpl('clicky'),
267 'server_announce': get_tmpl('server_announce'),
267 'server_announce': get_tmpl('server_announce'),
268 'flash_filtering': get_tmpl('flash_filtering')
268 'flash_filtering': get_tmpl('flash_filtering')
269 }[type]
269 }[type]
270 };
270 };
271
271
272 $('#pre_template').select2({
272 $('#pre_template').select2({
273 containerCssClass: 'drop-menu',
273 containerCssClass: 'drop-menu',
274 dropdownCssClass: 'drop-menu-dropdown',
274 dropdownCssClass: 'drop-menu-dropdown',
275 dropdownAutoWidth: true,
275 dropdownAutoWidth: true,
276 minimumResultsForSearch: -1
276 minimumResultsForSearch: -1
277 });
277 });
278
278
279 $('#post_template').select2({
279 $('#post_template').select2({
280 containerCssClass: 'drop-menu',
280 containerCssClass: 'drop-menu',
281 dropdownCssClass: 'drop-menu-dropdown',
281 dropdownCssClass: 'drop-menu-dropdown',
282 dropdownAutoWidth: true,
282 dropdownAutoWidth: true,
283 minimumResultsForSearch: -1
283 minimumResultsForSearch: -1
284 });
284 });
285
285
286 $('#post_template').on('change', function(e){
286 $('#post_template').on('change', function(e){
287 var sel = this.value;
287 var sel = this.value;
288 post_cm.setValue(get_data(sel, post_old))
288 post_cm.setValue(get_data(sel, post_old))
289 });
289 });
290
290
291 $('#pre_template').on('change', function(e){
291 $('#pre_template').on('change', function(e){
292 var sel = this.value;
292 var sel = this.value;
293 pre_cm.setValue(get_data(sel, pre_old))
293 pre_cm.setValue(get_data(sel, pre_old))
294 })
294 })
295 </script>
295 </script>
General Comments 0
You need to be logged in to leave comments. Login now