##// END OF EJS Templates
implemented manual repo rescann and remapping
marcink -
r348:e8fc8754 default
parent child Browse files
Show More
@@ -1,93 +1,109 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on July 14, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 from pylons import request, session, tmpl_context as c, url
26 from pylons import request, session, tmpl_context as c, url, app_globals as g
27 27 from pylons.controllers.util import abort, redirect
28 28 from pylons.i18n.translation import _
29 29 from pylons_app.lib import helpers as h
30 30 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator
31 31 from pylons_app.lib.base import BaseController, render
32 from pylons_app.lib.utils import repo2db_mapper, invalidate_cache
32 33 from pylons_app.model.db import User, UserLog
33 34 from pylons_app.model.forms import UserForm
35 from pylons_app.model.hg_model import HgModel
34 36 from pylons_app.model.user_model import UserModel
35 37 import formencode
36 38 import logging
37 39
38 40 log = logging.getLogger(__name__)
39 41
40 42
41 43 class SettingsController(BaseController):
42 44 """REST Controller styled on the Atom Publishing Protocol"""
43 45 # To properly map this controller, ensure your config/routing.py
44 46 # file has a resource setup:
45 47 # map.resource('setting', 'settings', controller='admin/settings',
46 48 # path_prefix='/admin', name_prefix='admin_')
47 49
48 50
49 51 @LoginRequired()
50 52 #@HasPermissionAllDecorator('hg.admin')
51 53 def __before__(self):
52 54 c.admin_user = session.get('admin_user')
53 55 c.admin_username = session.get('admin_username')
54 56 super(SettingsController, self).__before__()
55 57
56 58 def index(self, format='html'):
57 59 """GET /admin/settings: All items in the collection"""
58 60 # url('admin_settings')
59 61 return render('admin/settings/settings.html')
60 62
61 63 def create(self):
62 64 """POST /admin/settings: Create a new item"""
63 65 # url('admin_settings')
64 66
65 67 def new(self, format='html'):
66 68 """GET /admin/settings/new: Form to create a new item"""
67 69 # url('admin_new_setting')
68 70
69 71 def update(self, id):
70 72 """PUT /admin/settings/id: Update an existing item"""
71 73 # Forms posted to this method should contain a hidden field:
72 74 # <input type="hidden" name="_method" value="PUT" />
73 75 # Or using helpers:
74 76 # h.form(url('admin_setting', id=ID),
75 77 # method='put')
76 78 # url('admin_setting', id=ID)
79 if id == 'mapping':
80 rm_obsolete = request.POST.get('destroy', False)
81 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
82
83 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
84 repo2db_mapper(initial, rm_obsolete)
85 invalidate_cache('cached_repo_list')
86
87
88 return redirect(url('admin_settings'))
89
90
91
92
77 93
78 94 def delete(self, id):
79 95 """DELETE /admin/settings/id: Delete an existing item"""
80 96 # Forms posted to this method should contain a hidden field:
81 97 # <input type="hidden" name="_method" value="DELETE" />
82 98 # Or using helpers:
83 99 # h.form(url('admin_setting', id=ID),
84 100 # method='delete')
85 101 # url('admin_setting', id=ID)
86 102
87 103 def show(self, id, format='html'):
88 104 """GET /admin/settings/id: Show a specific item"""
89 105 # url('admin_setting', id=ID)
90 106
91 107 def edit(self, id, format='html'):
92 108 """GET /admin/settings/id/edit: Form to edit an existing item"""
93 109 # url('admin_edit_setting', id=ID)
@@ -1,202 +1,210 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 from beaker.cache import cache_region
20 20
21 21 """
22 22 Created on April 18, 2010
23 23 Utilities for hg app
24 24 @author: marcink
25 25 """
26 26
27 27 import os
28 28 import logging
29 29 from mercurial import ui, config, hg
30 30 from mercurial.error import RepoError
31 31 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
32 32 from pylons_app.model.meta import Session
33 33 log = logging.getLogger(__name__)
34 34
35 35
36 36 def get_repo_slug(request):
37 37 return request.environ['pylons.routes_dict'].get('repo_name')
38 38
39 39 def is_mercurial(environ):
40 40 """
41 41 Returns True if request's target is mercurial server - header
42 42 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
43 43 """
44 44 http_accept = environ.get('HTTP_ACCEPT')
45 45 if http_accept and http_accept.startswith('application/mercurial'):
46 46 return True
47 47 return False
48 48
49 49 def check_repo_dir(paths):
50 50 repos_path = paths[0][1].split('/')
51 51 if repos_path[-1] in ['*', '**']:
52 52 repos_path = repos_path[:-1]
53 53 if repos_path[0] != '/':
54 54 repos_path[0] = '/'
55 55 if not os.path.isdir(os.path.join(*repos_path)):
56 56 raise Exception('Not a valid repository in %s' % paths[0][1])
57 57
58 58 def check_repo_fast(repo_name, base_path):
59 59 if os.path.isdir(os.path.join(base_path, repo_name)):return False
60 60 return True
61 61
62 62 def check_repo(repo_name, base_path, verify=True):
63 63
64 64 repo_path = os.path.join(base_path, repo_name)
65 65
66 66 try:
67 67 if not check_repo_fast(repo_name, base_path):
68 68 return False
69 69 r = hg.repository(ui.ui(), repo_path)
70 70 if verify:
71 71 hg.verify(r)
72 72 #here we hnow that repo exists it was verified
73 73 log.info('%s repo is already created', repo_name)
74 74 return False
75 75 except RepoError:
76 76 #it means that there is no valid repo there...
77 77 log.info('%s repo is free for creation', repo_name)
78 78 return True
79 79
80 80
81 81 @cache_region('super_short_term', 'cached_hg_ui')
82 82 def get_hg_ui_cached():
83 83 sa = Session()
84 84 return sa.query(HgAppUi).all()
85 85
86 86 def get_hg_settings():
87 87 sa = Session()
88 88 ret = sa.query(HgAppSettings).scalar()
89 89 if not ret:
90 90 raise Exception('Could not get application settings !')
91 91 return ret
92 92
93 93 def make_ui(read_from='file', path=None, checkpaths=True):
94 94 """
95 95 A function that will read python rc files or database
96 96 and make an mercurial ui object from read options
97 97
98 98 @param path: path to mercurial config file
99 99 @param checkpaths: check the path
100 100 @param read_from: read from 'file' or 'db'
101 101 """
102 102 #propagated from mercurial documentation
103 103 sections = ['alias', 'auth',
104 104 'decode/encode', 'defaults',
105 105 'diff', 'email',
106 106 'extensions', 'format',
107 107 'merge-patterns', 'merge-tools',
108 108 'hooks', 'http_proxy',
109 109 'smtp', 'patch',
110 110 'paths', 'profiling',
111 111 'server', 'trusted',
112 112 'ui', 'web', ]
113 113 baseui = ui.ui()
114 114
115 115
116 116 if read_from == 'file':
117 117 if not os.path.isfile(path):
118 118 log.warning('Unable to read config file %s' % path)
119 119 return False
120 120
121 121 cfg = config.config()
122 122 cfg.read(path)
123 123 for section in sections:
124 124 for k, v in cfg.items(section):
125 125 baseui.setconfig(section, k, v)
126 126 if checkpaths:check_repo_dir(cfg.items('paths'))
127 127
128 128
129 129 elif read_from == 'db':
130 130 hg_ui = get_hg_ui_cached()
131 131 for ui_ in hg_ui:
132 132 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
133 133
134 134
135 135 return baseui
136 136
137 137
138 138 def set_hg_app_config(config):
139 139 hgsettings = get_hg_settings()
140 140 config['hg_app_auth_realm'] = hgsettings.app_auth_realm
141 141 config['hg_app_name'] = hgsettings.app_title
142 142
143 143 def invalidate_cache(name, *args):
144 144 """Invalidates given name cache"""
145 145
146 146 from beaker.cache import region_invalidate
147 147 log.info('INVALIDATING CACHE FOR %s', name)
148 148
149 149 """propagate our arguments to make sure invalidation works. First
150 150 argument has to be the name of cached func name give to cache decorator
151 151 without that the invalidation would not work"""
152 152 tmp = [name]
153 153 tmp.extend(args)
154 154 args = tuple(tmp)
155 155
156 156 if name == 'cached_repo_list':
157 157 from pylons_app.model.hg_model import _get_repos_cached
158 158 region_invalidate(_get_repos_cached, None, *args)
159 159
160 160 if name == 'full_changelog':
161 161 from pylons_app.model.hg_model import _full_changelog_cached
162 162 region_invalidate(_full_changelog_cached, None, *args)
163 163
164 164 from vcs.backends.base import BaseChangeset
165 165 from vcs.utils.lazy import LazyProperty
166 166 class EmptyChangeset(BaseChangeset):
167 167
168 168 revision = -1
169 169 message = ''
170 170
171 171 @LazyProperty
172 172 def raw_id(self):
173 173 """
174 174 Returns raw string identifing this changeset, useful for web
175 175 representation.
176 176 """
177 177 return '0' * 12
178 178
179 179
180 def repo2db_mapper(initial_repo_list):
180 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
181 181 """
182 182 maps all found repositories into db
183 183 """
184 from pylons_app.model.meta import Session
185 184 from pylons_app.model.repo_model import RepoModel
186 185
187 186 sa = Session()
188 187 user = sa.query(User).filter(User.admin == True).first()
189 188
190 189 rm = RepoModel()
191 190
192 191 for name, repo in initial_repo_list.items():
193 192 if not sa.query(Repository).get(name):
194 193 log.info('repository %s not found creating default', name)
195 194
196 195 form_data = {
197 196 'repo_name':name,
198 197 'description':repo.description if repo.description != 'unknown' else \
199 198 'auto description for %s' % name,
200 199 'private':False
201 200 }
202 201 rm.create(form_data, user, just_db=True)
202
203
204 if remove_obsolete:
205 #remove from database those repositories that are not in the filesystem
206 for repo in sa.query(Repository).all():
207 if repo.repo_name not in initial_repo_list.keys():
208 sa.delete(repo)
209 sa.commit()
210
@@ -1,21 +1,53 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Settings administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 10 ${_('Settings')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('settings')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('Settings administration')}</h2>
19 19
20 ${h.form(url('admin_setting', id='mapping'),method='put')}
21 <table class="table_disp">
22 <tr class="header">
23 <td colspan="2">${_('Remap andv rescan repositories')}</td>
24 </tr>
25 <tr align="right">
26 <td><span class="tooltip" tooltip_title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
27 ${_('destroy old data')}</span> ${h.checkbox('destroy',True)}</td>
28 <td>${h.submit('rescan','rescan repositories')}</td>
29 </table>
30 ${h.end_form()}
31 <br/>
32 ${h.form(url('admin_setting', id='global'),method='put')}
33 <table class="table_disp">
34 <tr class="header">
35 <td colspan="3">${_('Global application settings')}</td>
36 </tr>
37 <tr>
38 <td>${_('Application name')}</td>
39 <td>${h.text('app_title')}</td>
40 </tr>
41 <tr>
42 <td>${_('Realm text')}</td>
43 <td>${h.text('app_auth_realm')}</td>
44 </tr>
45 <tr>
46 <td></td>
47 <td>${h.submit('save','save settings')}</td>
48 </tr>
49 </table>
50 ${h.end_form()}
51
20 52 </div>
21 53 </%def>
General Comments 0
You need to be logged in to leave comments. Login now