diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -42,8 +42,8 @@ from rhodecode.model.repo import ReadmeF log = logging.getLogger(__name__) -ADMIN_PREFIX = '/_admin' -STATIC_FILE_PREFIX = '/_static' +ADMIN_PREFIX: str = '/_admin' +STATIC_FILE_PREFIX: str = '/_static' URL_NAME_REQUIREMENTS = { # group name can have a slash in them, but they must not end with a slash diff --git a/rhodecode/apps/_base/navigation.py b/rhodecode/apps/_base/navigation.py --- a/rhodecode/apps/_base/navigation.py +++ b/rhodecode/apps/_base/navigation.py @@ -101,8 +101,6 @@ class NavigationRegistry(object): 'admin_settings_sessions'), NavEntry('open_source', _('Open Source Licenses'), 'admin_settings_open_source'), - NavEntry('automation', _('Automation'), - 'admin_settings_automation') ] _labs_entry = NavEntry('labs', _('Labs'), diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -18,6 +18,8 @@ from rhodecode.apps._base import ADMIN_PREFIX +from rhodecode.apps._base.navigation import includeme as nav_includeme +from rhodecode.apps.admin.views.main_views import AdminMainView def admin_routes(config): @@ -26,9 +28,10 @@ def admin_routes(config): """ from rhodecode.apps.admin.views.audit_logs import AdminAuditLogsView from rhodecode.apps.admin.views.artifacts import AdminArtifactsView + from rhodecode.apps.admin.views.automation import AdminAutomationView + from rhodecode.apps.admin.views.scheduler import AdminSchedulerView from rhodecode.apps.admin.views.defaults import AdminDefaultSettingsView from rhodecode.apps.admin.views.exception_tracker import ExceptionsTrackerView - from rhodecode.apps.admin.views.main_views import AdminMainView from rhodecode.apps.admin.views.open_source_licenses import OpenSourceLicensesAdminSettingsView from rhodecode.apps.admin.views.permissions import AdminPermissionsView from rhodecode.apps.admin.views.process_management import AdminProcessManagementView @@ -76,6 +79,7 @@ def admin_routes(config): attr='artifacts', route_name='admin_artifacts_show_all', request_method='GET', renderer='rhodecode:templates/admin/artifacts/artifacts.mako') + # EE views config.add_route( name='admin_artifacts_show_info', @@ -87,6 +91,26 @@ def admin_routes(config): name='admin_artifacts_update', pattern=ADMIN_PREFIX + '/artifacts/{uid}/update') + # Automation EE feature + config.add_route( + 'admin_automation', + pattern=ADMIN_PREFIX + '/automation') + config.add_view( + AdminAutomationView, + attr='automation', + route_name='admin_automation', request_method='GET', + renderer='rhodecode:templates/admin/automation/automation.mako') + + # Scheduler EE feature + config.add_route( + 'admin_scheduler', + pattern=ADMIN_PREFIX + '/scheduler') + config.add_view( + AdminSchedulerView, + attr='scheduler', + route_name='admin_scheduler', request_method='GET', + renderer='rhodecode:templates/admin/scheduler/scheduler.mako') + config.add_route( name='admin_settings_open_source', pattern='/settings/open_source') @@ -440,16 +464,6 @@ def admin_routes(config): route_name='admin_settings_labs_update', request_method='POST', renderer='rhodecode:templates/admin/settings/settings.mako') - # Automation EE feature - config.add_route( - 'admin_settings_automation', - pattern=ADMIN_PREFIX + '/settings/automation') - config.add_view( - AdminSettingsView, - attr='settings_automation', - route_name='admin_settings_automation', request_method='GET', - renderer='rhodecode:templates/admin/settings/settings.mako') - # global permissions config.add_route( @@ -1039,9 +1053,6 @@ def admin_routes(config): def includeme(config): - from rhodecode.apps._base.navigation import includeme as nav_includeme - from rhodecode.apps.admin.views.main_views import AdminMainView - # Create admin navigation registry and add it to the pyramid registry. nav_includeme(config) diff --git a/rhodecode/apps/admin/views/automation.py b/rhodecode/apps/admin/views/automation.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/admin/views/automation.py @@ -0,0 +1,38 @@ +# Copyright (C) 2016-2023 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import logging + +from rhodecode.apps._base import BaseAppView +from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator + +log = logging.getLogger(__name__) + + +class AdminAutomationView(BaseAppView): + + def load_default_context(self): + c = self._get_local_tmpl_context() + return c + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + def automation(self): + c = self.load_default_context() + c.active = 'automation' + return self._get_template_context(c) diff --git a/rhodecode/apps/admin/views/scheduler.py b/rhodecode/apps/admin/views/scheduler.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/admin/views/scheduler.py @@ -0,0 +1,38 @@ +# Copyright (C) 2016-2023 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import logging + +from rhodecode.apps._base import BaseAppView +from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator + +log = logging.getLogger(__name__) + + +class AdminSchedulerView(BaseAppView): + + def load_default_context(self): + c = self._get_local_tmpl_context() + return c + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + def scheduler(self): + c = self.load_default_context() + c.active = 'scheduler' + return self._get_template_context(c) diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -248,8 +248,9 @@ class AdminSettingsView(BaseAppView): added, removed = repo2db_mapper(filesystem_repos, rm_obsolete) PermissionModel().trigger_permission_flush() - def _repr(l): - return ', '.join(map(safe_str, l)) or '-' + def _repr(rm_repo): + return ', '.join(map(safe_str, rm_repo)) or '-' + h.flash(_('Repositories successfully ' 'rescanned added: %s ; removed: %s') % (_repr(added), _repr(removed)), @@ -623,14 +624,6 @@ class AdminSettingsView(BaseAppView): @LoginRequired() @HasPermissionAllDecorator('hg.admin') - def settings_automation(self): - c = self.load_default_context() - c.active = 'automation' - - return self._get_template_context(c) - - @LoginRequired() - @HasPermissionAllDecorator('hg.admin') def settings_labs(self): c = self.load_default_context() if not c.labs_active: diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -413,6 +413,7 @@ class RepoFilesView(RepoAppView): archive_cache_disable = self.request.GET.get('no_cache') d_cache = get_archival_cache_store(config=CONFIG) + # NOTE: we get the config to pass to a call to lazy-init the SAME type of cache on vcsserver d_cache_conf = get_archival_config(config=CONFIG) diff --git a/rhodecode/events/pullrequest.py b/rhodecode/events/pullrequest.py --- a/rhodecode/events/pullrequest.py +++ b/rhodecode/events/pullrequest.py @@ -30,6 +30,9 @@ class PullRequestEvent(RepoEvent): :param pullrequest: a :class:`PullRequest` instance """ + name = 'pullrequest-event' + display_name = lazy_ugettext('pullrequest generic event') + description = lazy_ugettext('All events within a context of a pull request') def __init__(self, pullrequest): super().__init__(pullrequest.target_repo) diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py --- a/rhodecode/events/repo.py +++ b/rhodecode/events/repo.py @@ -21,7 +21,7 @@ import logging import datetime from rhodecode.translation import lazy_ugettext -from rhodecode.model.db import User, Repository, Session +from rhodecode.model.db import User, Repository from rhodecode.events.base import RhodeCodeIntegrationEvent from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError @@ -34,7 +34,7 @@ def _commits_as_dict(event, commit_ids, :param event: class calling this method :param commit_ids: commits to get - :param repos: list of repos to check + :param repos: a list of repos to check """ from rhodecode.lib.utils2 import extract_mentioned_users from rhodecode.lib.helpers import ( @@ -154,11 +154,12 @@ def _issues_as_dict(commits): class RepoEvent(RhodeCodeIntegrationEvent): """ Base class for events acting on a repository. - - :param repo: a :class:`Repository` instance """ def __init__(self, repo): + """ + :param repo: a :class:`Repository` instance + """ super().__init__() self.repo = repo @@ -299,6 +300,9 @@ class RepoVCSEvent(RepoEvent): """ Base class for events triggered by the VCS """ + name = '' + display_name = 'generic_vcs_event' + def __init__(self, repo_name, extras): self.repo = Repository.get_by_repo_name(repo_name) if not self.repo: diff --git a/rhodecode/lib/celerylib/__init__.py b/rhodecode/lib/celerylib/__init__.py --- a/rhodecode/lib/celerylib/__init__.py +++ b/rhodecode/lib/celerylib/__init__.py @@ -44,7 +44,7 @@ def run_task(task, *args, **kwargs): import celery log.debug('Got task `%s` for execution, celery mode enabled:%s', task, rhodecode.CELERY_ENABLED) if task is None: - raise ValueError('Got non-existing task for execution') + raise ValueError(f'Got non-existing task: {task} for execution') exec_mode = 'sync' allow_async = True diff --git a/rhodecode/lib/jsonalchemy.py b/rhodecode/lib/jsonalchemy.py --- a/rhodecode/lib/jsonalchemy.py +++ b/rhodecode/lib/jsonalchemy.py @@ -19,9 +19,7 @@ import sqlalchemy from sqlalchemy import UnicodeText -from sqlalchemy.ext.mutable import Mutable, \ - MutableList as MutationList, \ - MutableDict as MutationDict +from sqlalchemy.ext.mutable import Mutable, MutableList, MutableDict from rhodecode.lib import ext_json @@ -30,7 +28,7 @@ class JsonRaw(str): """ Allows interacting with a JSON types field using a raw string. - For example:: + For example: db_instance = JsonTable() db_instance.enabled = True db_instance.json_data = JsonRaw('{"a": 4}') @@ -100,7 +98,7 @@ class MutationObj(Mutable): return MutationList.coerce(key, value) return value - def de_coerce(self): + def de_coerce(self) -> "MutationObj": return self @classmethod @@ -153,6 +151,16 @@ class MutationObj(Mutable): propagate=True) +class MutationList(MutableList): + def de_coerce(self): + return list(self) + + +class MutationDict(MutableDict): + def de_coerce(self): + return dict(self) + + def JsonType(impl=None, **kwargs): """ Helper for using a mutation obj, it allows to use .with_variant easily. diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -5378,14 +5378,14 @@ class ScheduleEntry(Base, BaseModel): except ValueError: return dict() - def _as_raw(self, val, indent=None): + def _as_raw(self, val, indent=False): if hasattr(val, 'de_coerce'): val = val.de_coerce() if val: if indent: - ext_json.formatted_json(val) + val = ext_json.formatted_str_json(val) else: - val = ext_json.json.dumps(val) + val = ext_json.str_json(val) return val @@ -5393,10 +5393,10 @@ class ScheduleEntry(Base, BaseModel): def schedule_definition_raw(self): return self._as_raw(self.schedule_definition) - def args_raw(self, indent=None): + def args_raw(self, indent=False): return self._as_raw(self.task_args, indent) - def kwargs_raw(self, indent=None): + def kwargs_raw(self, indent=False): return self._as_raw(self.task_kwargs, indent) def __repr__(self): @@ -5610,6 +5610,19 @@ class FileStore(Base, BaseModel): repo_group = relationship('RepoGroup', lazy='joined') @classmethod + def get_scope(cls, scope_type, scope_id): + if scope_type == 'repo': + return f'repo:{scope_id}' + elif scope_type == 'repo-group': + return f'repo-group:{scope_id}' + elif scope_type == 'user': + return f'user:{scope_id}' + elif scope_type == 'user-group': + return f'user-group:{scope_id}' + else: + return scope_type + + @classmethod def get_by_store_uid(cls, file_store_uid, safe=False): if safe: return FileStore.query().filter(FileStore.file_uid == file_store_uid).first() diff --git a/rhodecode/model/validation_schema/validators.py b/rhodecode/model/validation_schema/validators.py --- a/rhodecode/model/validation_schema/validators.py +++ b/rhodecode/model/validation_schema/validators.py @@ -25,7 +25,7 @@ import colander from rhodecode.translation import _ from rhodecode.lib.utils2 import glob2re from rhodecode.lib.str_utils import safe_str -from rhodecode.lib.ext_json import json +from rhodecode.lib.ext_json import json, sjson log = logging.getLogger(__name__) @@ -152,8 +152,9 @@ def json_validator(node, value): def json_validator_with_exc(node, value): + try: json.loads(value) except (Exception,) as e: - msg = _(f'Please enter a valid json object: `{e}`') + msg = _(f'Please enter a valid json object type={type(value)}: `{e}`') raise colander.Invalid(node, msg) diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -20,6 +20,8 @@ function registerRCRoutes() { pyroutes.register('admin_artifacts_update', '/_admin/artifacts/%(uid)s/update', ['uid']); pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']); pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []); + pyroutes.register('admin_automation', '/_admin/automation', []); + pyroutes.register('admin_automation_update', '/_admin/automation/%(entry_id)s/update', ['entry_id']); pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []); pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []); pyroutes.register('admin_home', '/_admin', []); @@ -37,9 +39,9 @@ function registerRCRoutes() { pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []); pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []); pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []); + pyroutes.register('admin_scheduler', '/_admin/scheduler', []); + pyroutes.register('admin_scheduler_show_tasks', '/_admin/scheduler/_tasks', []); pyroutes.register('admin_settings', '/_admin/settings', []); - pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []); - pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']); pyroutes.register('admin_settings_email', '/_admin/settings/email', []); pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []); pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []); @@ -66,14 +68,12 @@ function registerRCRoutes() { pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []); pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []); pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []); - pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []); - pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']); - pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']); - pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']); - pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []); - pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []); - pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []); - pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']); + pyroutes.register('admin_settings_scheduler_create', '/_admin/scheduler/create', []); + pyroutes.register('admin_settings_scheduler_delete', '/_admin/scheduler/%(schedule_id)s/delete', ['schedule_id']); + pyroutes.register('admin_settings_scheduler_edit', '/_admin/scheduler/%(schedule_id)s', ['schedule_id']); + pyroutes.register('admin_settings_scheduler_execute', '/_admin/scheduler/%(schedule_id)s/execute', ['schedule_id']); + pyroutes.register('admin_settings_scheduler_new', '/_admin/scheduler/new', []); + pyroutes.register('admin_settings_scheduler_update', '/_admin/scheduler/%(schedule_id)s/update', ['schedule_id']); pyroutes.register('admin_settings_search', '/_admin/settings/search', []); pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []); pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []); @@ -308,7 +308,6 @@ function registerRCRoutes() { pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']); pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']); pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']); - pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']); pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); @@ -362,6 +361,7 @@ function registerRCRoutes() { pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']); pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']); pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']); + pyroutes.register('repo_settings_quick_actions', '/%(repo_name)s/settings/quick-action', ['repo_name']); pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']); pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']); diff --git a/rhodecode/templates/admin/artifacts/artifacts.mako b/rhodecode/templates/admin/artifacts/artifacts.mako --- a/rhodecode/templates/admin/artifacts/artifacts.mako +++ b/rhodecode/templates/admin/artifacts/artifacts.mako @@ -27,7 +27,10 @@

${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='sales@rhodecode.com')|n}

- +

+ Artifacts are a binary file storage within RhodeCode that allows asset management next to version control system with fine-grained access control. + This functionality allows release builds or other types of binary asset to be stored and managed by RhodeCode. +

diff --git a/rhodecode/templates/admin/settings/settings_automation.mako b/rhodecode/templates/admin/automation/automation.mako rename from rhodecode/templates/admin/settings/settings_automation.mako rename to rhodecode/templates/admin/automation/automation.mako --- a/rhodecode/templates/admin/settings/settings_automation.mako +++ b/rhodecode/templates/admin/automation/automation.mako @@ -1,9 +1,37 @@ -
-
-

${_('Admin Automation')}

+<%inherit file="/base/base.mako"/> + +<%def name="title()"> + ${_('Artifacts Admin')} + %if c.rhodecode_name: + · ${h.branding(c.rhodecode_name)} + %endif + + +<%def name="breadcrumbs_links()"> + +<%def name="menu_bar_nav()"> + ${self.menu_items(active='admin')} + + +<%def name="menu_bar_subnav()"> + ${self.admin_menu(active='automation')} + + +<%def name="main()"> + +
+ +
+
+

${_('Automation Administration.')}

+
+
+

${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='sales@rhodecode.com')|n}

+ admin-automation +
-
-

${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='sales@rhodecode.com')|n}

- -
+
+ + + diff --git a/rhodecode/templates/admin/gists/gist_index.mako b/rhodecode/templates/admin/gists/gist_index.mako --- a/rhodecode/templates/admin/gists/gist_index.mako +++ b/rhodecode/templates/admin/gists/gist_index.mako @@ -39,7 +39,7 @@ % if c.rhodecode_user.username != h.DEFAULT_USER: % endif diff --git a/rhodecode/templates/admin/integrations/list.mako b/rhodecode/templates/admin/integrations/list.mako --- a/rhodecode/templates/admin/integrations/list.mako +++ b/rhodecode/templates/admin/integrations/list.mako @@ -61,7 +61,7 @@ create_url = h.route_path('global_integrations_new') %>

- ${_(u'Create new integration')} + ${_('Create new integration')}

@@ -99,7 +99,7 @@ %> %endif - ${_(u'Create one')} + ${_('Create one')} %endif diff --git a/rhodecode/templates/admin/main.mako b/rhodecode/templates/admin/main.mako --- a/rhodecode/templates/admin/main.mako +++ b/rhodecode/templates/admin/main.mako @@ -39,7 +39,7 @@ @@ -48,7 +48,7 @@ diff --git a/rhodecode/templates/admin/repo_groups/repo_groups.mako b/rhodecode/templates/admin/repo_groups/repo_groups.mako --- a/rhodecode/templates/admin/repo_groups/repo_groups.mako +++ b/rhodecode/templates/admin/repo_groups/repo_groups.mako @@ -27,7 +27,7 @@ diff --git a/rhodecode/templates/admin/repos/repos.mako b/rhodecode/templates/admin/repos/repos.mako --- a/rhodecode/templates/admin/repos/repos.mako +++ b/rhodecode/templates/admin/repos/repos.mako @@ -27,7 +27,7 @@ diff --git a/rhodecode/templates/admin/scheduler/scheduler.mako b/rhodecode/templates/admin/scheduler/scheduler.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/admin/scheduler/scheduler.mako @@ -0,0 +1,39 @@ +<%inherit file="/base/base.mako"/> + +<%def name="title()"> + ${_('Artifacts Admin')} + %if c.rhodecode_name: + · ${h.branding(c.rhodecode_name)} + %endif + + +<%def name="breadcrumbs_links()"> + +<%def name="menu_bar_nav()"> + ${self.menu_items(active='admin')} + + +<%def name="menu_bar_subnav()"> + ${self.admin_menu(active='scheduler')} + + +<%def name="main()"> + +
+ +
+
+

${_('Scheduler Administration.')}

+
+
+

${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='sales@rhodecode.com')|n}

+

+ Scheduler enables management of automation tasks, and defining new custom cron-like actions to be executed within RhodeCode system. +

+
+
+ +
+ + + diff --git a/rhodecode/templates/admin/user_groups/user_groups.mako b/rhodecode/templates/admin/user_groups/user_groups.mako --- a/rhodecode/templates/admin/user_groups/user_groups.mako +++ b/rhodecode/templates/admin/user_groups/user_groups.mako @@ -27,7 +27,7 @@ diff --git a/rhodecode/templates/admin/users/users.mako b/rhodecode/templates/admin/users/users.mako --- a/rhodecode/templates/admin/users/users.mako +++ b/rhodecode/templates/admin/users/users.mako @@ -27,7 +27,7 @@ diff --git a/rhodecode/templates/base/base.mako b/rhodecode/templates/base/base.mako --- a/rhodecode/templates/base/base.mako +++ b/rhodecode/templates/base/base.mako @@ -107,7 +107,7 @@
${len(c.auth_user.repository_groups_admin)} % if c.can_create_repo_group: - ${_(u'Add Repository Group')} + ${_('Add Repository Group')} % endif
${len(c.auth_user.user_groups_admin)} % if c.can_create_user_group: - ${_(u'Add User Group')} + ${_('Add User Group')} % endif