##// END OF EJS Templates
licensing updates, code cleanups
marcink -
r252:3782a6d6 default
parent child Browse files
Show More
@@ -1,340 +1,340 b''
1 1 GNU GENERAL PUBLIC LICENSE
2 2 Version 2, June 1991
3 3
4 4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 5 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 6 Everyone is permitted to copy and distribute verbatim copies
7 7 of this license document, but changing it is not allowed.
8 8
9 9 Preamble
10 10
11 11 The licenses for most software are designed to take away your
12 12 freedom to share and change it. By contrast, the GNU General Public
13 13 License is intended to guarantee your freedom to share and change free
14 14 software--to make sure the software is free for all its users. This
15 15 General Public License applies to most of the Free Software
16 16 Foundation's software and to any other program whose authors commit to
17 17 using it. (Some other Free Software Foundation software is covered by
18 18 the GNU Library General Public License instead.) You can apply it to
19 19 your programs, too.
20 20
21 21 When we speak of free software, we are referring to freedom, not
22 22 price. Our General Public Licenses are designed to make sure that you
23 23 have the freedom to distribute copies of free software (and charge for
24 24 this service if you wish), that you receive source code or can get it
25 25 if you want it, that you can change the software or use pieces of it
26 26 in new free programs; and that you know you can do these things.
27 27
28 28 To protect your rights, we need to make restrictions that forbid
29 29 anyone to deny you these rights or to ask you to surrender the rights.
30 30 These restrictions translate to certain responsibilities for you if you
31 31 distribute copies of the software, or if you modify it.
32 32
33 33 For example, if you distribute copies of such a program, whether
34 34 gratis or for a fee, you must give the recipients all the rights that
35 35 you have. You must make sure that they, too, receive or can get the
36 36 source code. And you must show them these terms so they know their
37 37 rights.
38 38
39 39 We protect your rights with two steps: (1) copyright the software, and
40 40 (2) offer you this license which gives you legal permission to copy,
41 41 distribute and/or modify the software.
42 42
43 43 Also, for each author's protection and ours, we want to make certain
44 44 that everyone understands that there is no warranty for this free
45 45 software. If the software is modified by someone else and passed on, we
46 46 want its recipients to know that what they have is not the original, so
47 47 that any problems introduced by others will not reflect on the original
48 48 authors' reputations.
49 49
50 50 Finally, any free program is threatened constantly by software
51 51 patents. We wish to avoid the danger that redistributors of a free
52 52 program will individually obtain patent licenses, in effect making the
53 53 program proprietary. To prevent this, we have made it clear that any
54 54 patent must be licensed for everyone's free use or not licensed at all.
55 55
56 56 The precise terms and conditions for copying, distribution and
57 57 modification follow.
58 58
59 59 GNU GENERAL PUBLIC LICENSE
60 60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 61
62 62 0. This License applies to any program or other work which contains
63 63 a notice placed by the copyright holder saying it may be distributed
64 64 under the terms of this General Public License. The "Program", below,
65 65 refers to any such program or work, and a "work based on the Program"
66 66 means either the Program or any derivative work under copyright law:
67 67 that is to say, a work containing the Program or a portion of it,
68 68 either verbatim or with modifications and/or translated into another
69 69 language. (Hereinafter, translation is included without limitation in
70 70 the term "modification".) Each licensee is addressed as "you".
71 71
72 72 Activities other than copying, distribution and modification are not
73 73 covered by this License; they are outside its scope. The act of
74 74 running the Program is not restricted, and the output from the Program
75 75 is covered only if its contents constitute a work based on the
76 76 Program (independent of having been made by running the Program).
77 77 Whether that is true depends on what the Program does.
78 78
79 79 1. You may copy and distribute verbatim copies of the Program's
80 80 source code as you receive it, in any medium, provided that you
81 81 conspicuously and appropriately publish on each copy an appropriate
82 82 copyright notice and disclaimer of warranty; keep intact all the
83 83 notices that refer to this License and to the absence of any warranty;
84 84 and give any other recipients of the Program a copy of this License
85 85 along with the Program.
86 86
87 87 You may charge a fee for the physical act of transferring a copy, and
88 88 you may at your option offer warranty protection in exchange for a fee.
89 89
90 90 2. You may modify your copy or copies of the Program or any portion
91 91 of it, thus forming a work based on the Program, and copy and
92 92 distribute such modifications or work under the terms of Section 1
93 93 above, provided that you also meet all of these conditions:
94 94
95 95 a) You must cause the modified files to carry prominent notices
96 96 stating that you changed the files and the date of any change.
97 97
98 98 b) You must cause any work that you distribute or publish, that in
99 99 whole or in part contains or is derived from the Program or any
100 100 part thereof, to be licensed as a whole at no charge to all third
101 101 parties under the terms of this License.
102 102
103 103 c) If the modified program normally reads commands interactively
104 104 when run, you must cause it, when started running for such
105 105 interactive use in the most ordinary way, to print or display an
106 106 announcement including an appropriate copyright notice and a
107 107 notice that there is no warranty (or else, saying that you provide
108 108 a warranty) and that users may redistribute the program under
109 109 these conditions, and telling the user how to view a copy of this
110 110 License. (Exception: if the Program itself is interactive but
111 111 does not normally print such an announcement, your work based on
112 112 the Program is not required to print an announcement.)
113 113
114 114 These requirements apply to the modified work as a whole. If
115 115 identifiable sections of that work are not derived from the Program,
116 116 and can be reasonably considered independent and separate works in
117 117 themselves, then this License, and its terms, do not apply to those
118 118 sections when you distribute them as separate works. But when you
119 119 distribute the same sections as part of a whole which is a work based
120 120 on the Program, the distribution of the whole must be on the terms of
121 121 this License, whose permissions for other licensees extend to the
122 122 entire whole, and thus to each and every part regardless of who wrote it.
123 123
124 124 Thus, it is not the intent of this section to claim rights or contest
125 125 your rights to work written entirely by you; rather, the intent is to
126 126 exercise the right to control the distribution of derivative or
127 127 collective works based on the Program.
128 128
129 129 In addition, mere aggregation of another work not based on the Program
130 130 with the Program (or with a work based on the Program) on a volume of
131 131 a storage or distribution medium does not bring the other work under
132 132 the scope of this License.
133 133
134 134 3. You may copy and distribute the Program (or a work based on it,
135 135 under Section 2) in object code or executable form under the terms of
136 136 Sections 1 and 2 above provided that you also do one of the following:
137 137
138 138 a) Accompany it with the complete corresponding machine-readable
139 139 source code, which must be distributed under the terms of Sections
140 140 1 and 2 above on a medium customarily used for software interchange; or,
141 141
142 142 b) Accompany it with a written offer, valid for at least three
143 143 years, to give any third party, for a charge no more than your
144 144 cost of physically performing source distribution, a complete
145 145 machine-readable copy of the corresponding source code, to be
146 146 distributed under the terms of Sections 1 and 2 above on a medium
147 147 customarily used for software interchange; or,
148 148
149 149 c) Accompany it with the information you received as to the offer
150 150 to distribute corresponding source code. (This alternative is
151 151 allowed only for noncommercial distribution and only if you
152 152 received the program in object code or executable form with such
153 153 an offer, in accord with Subsection b above.)
154 154
155 155 The source code for a work means the preferred form of the work for
156 156 making modifications to it. For an executable work, complete source
157 157 code means all the source code for all modules it contains, plus any
158 158 associated interface definition files, plus the scripts used to
159 159 control compilation and installation of the executable. However, as a
160 160 special exception, the source code distributed need not include
161 161 anything that is normally distributed (in either source or binary
162 162 form) with the major components (compiler, kernel, and so on) of the
163 163 operating system on which the executable runs, unless that component
164 164 itself accompanies the executable.
165 165
166 166 If distribution of executable or object code is made by offering
167 167 access to copy from a designated place, then offering equivalent
168 168 access to copy the source code from the same place counts as
169 169 distribution of the source code, even though third parties are not
170 170 compelled to copy the source along with the object code.
171
171
172 172 4. You may not copy, modify, sublicense, or distribute the Program
173 173 except as expressly provided under this License. Any attempt
174 174 otherwise to copy, modify, sublicense or distribute the Program is
175 175 void, and will automatically terminate your rights under this License.
176 176 However, parties who have received copies, or rights, from you under
177 177 this License will not have their licenses terminated so long as such
178 178 parties remain in full compliance.
179 179
180 180 5. You are not required to accept this License, since you have not
181 181 signed it. However, nothing else grants you permission to modify or
182 182 distribute the Program or its derivative works. These actions are
183 183 prohibited by law if you do not accept this License. Therefore, by
184 184 modifying or distributing the Program (or any work based on the
185 185 Program), you indicate your acceptance of this License to do so, and
186 186 all its terms and conditions for copying, distributing or modifying
187 187 the Program or works based on it.
188 188
189 189 6. Each time you redistribute the Program (or any work based on the
190 190 Program), the recipient automatically receives a license from the
191 191 original licensor to copy, distribute or modify the Program subject to
192 192 these terms and conditions. You may not impose any further
193 193 restrictions on the recipients' exercise of the rights granted herein.
194 194 You are not responsible for enforcing compliance by third parties to
195 195 this License.
196 196
197 197 7. If, as a consequence of a court judgment or allegation of patent
198 198 infringement or for any other reason (not limited to patent issues),
199 199 conditions are imposed on you (whether by court order, agreement or
200 200 otherwise) that contradict the conditions of this License, they do not
201 201 excuse you from the conditions of this License. If you cannot
202 202 distribute so as to satisfy simultaneously your obligations under this
203 203 License and any other pertinent obligations, then as a consequence you
204 204 may not distribute the Program at all. For example, if a patent
205 205 license would not permit royalty-free redistribution of the Program by
206 206 all those who receive copies directly or indirectly through you, then
207 207 the only way you could satisfy both it and this License would be to
208 208 refrain entirely from distribution of the Program.
209 209
210 210 If any portion of this section is held invalid or unenforceable under
211 211 any particular circumstance, the balance of the section is intended to
212 212 apply and the section as a whole is intended to apply in other
213 213 circumstances.
214 214
215 215 It is not the purpose of this section to induce you to infringe any
216 216 patents or other property right claims or to contest validity of any
217 217 such claims; this section has the sole purpose of protecting the
218 218 integrity of the free software distribution system, which is
219 219 implemented by public license practices. Many people have made
220 220 generous contributions to the wide range of software distributed
221 221 through that system in reliance on consistent application of that
222 222 system; it is up to the author/donor to decide if he or she is willing
223 223 to distribute software through any other system and a licensee cannot
224 224 impose that choice.
225 225
226 226 This section is intended to make thoroughly clear what is believed to
227 227 be a consequence of the rest of this License.
228 228
229 229 8. If the distribution and/or use of the Program is restricted in
230 230 certain countries either by patents or by copyrighted interfaces, the
231 231 original copyright holder who places the Program under this License
232 232 may add an explicit geographical distribution limitation excluding
233 233 those countries, so that distribution is permitted only in or among
234 234 countries not thus excluded. In such case, this License incorporates
235 235 the limitation as if written in the body of this License.
236 236
237 237 9. The Free Software Foundation may publish revised and/or new versions
238 238 of the General Public License from time to time. Such new versions will
239 239 be similar in spirit to the present version, but may differ in detail to
240 240 address new problems or concerns.
241 241
242 242 Each version is given a distinguishing version number. If the Program
243 243 specifies a version number of this License which applies to it and "any
244 244 later version", you have the option of following the terms and conditions
245 245 either of that version or of any later version published by the Free
246 246 Software Foundation. If the Program does not specify a version number of
247 247 this License, you may choose any version ever published by the Free Software
248 248 Foundation.
249 249
250 250 10. If you wish to incorporate parts of the Program into other free
251 251 programs whose distribution conditions are different, write to the author
252 252 to ask for permission. For software which is copyrighted by the Free
253 253 Software Foundation, write to the Free Software Foundation; we sometimes
254 254 make exceptions for this. Our decision will be guided by the two goals
255 255 of preserving the free status of all derivatives of our free software and
256 256 of promoting the sharing and reuse of software generally.
257 257
258 258 NO WARRANTY
259 259
260 260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 261 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 262 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 263 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 264 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 265 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 266 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 267 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 268 REPAIR OR CORRECTION.
269 269
270 270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 271 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 272 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 273 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 274 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 275 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 276 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 277 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 278 POSSIBILITY OF SUCH DAMAGES.
279 279
280 280 END OF TERMS AND CONDITIONS
281
281
282 282 How to Apply These Terms to Your New Programs
283 283
284 284 If you develop a new program, and you want it to be of the greatest
285 285 possible use to the public, the best way to achieve this is to make it
286 286 free software which everyone can redistribute and change under these terms.
287 287
288 288 To do so, attach the following notices to the program. It is safest
289 289 to attach them to the start of each source file to most effectively
290 290 convey the exclusion of warranty; and each file should have at least
291 291 the "copyright" line and a pointer to where the full notice is found.
292 292
293 293 <one line to give the program's name and a brief idea of what it does.>
294 294 Copyright (C) <year> <name of author>
295 295
296 296 This program is free software; you can redistribute it and/or modify
297 297 it under the terms of the GNU General Public License as published by
298 298 the Free Software Foundation; either version 2 of the License, or
299 299 (at your option) any later version.
300 300
301 301 This program is distributed in the hope that it will be useful,
302 302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 304 GNU General Public License for more details.
305 305
306 306 You should have received a copy of the GNU General Public License
307 307 along with this program; if not, write to the Free Software
308 308 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309 309
310 310
311 311 Also add information on how to contact you by electronic and paper mail.
312 312
313 313 If the program is interactive, make it output a short notice like this
314 314 when it starts in an interactive mode:
315 315
316 316 Gnomovision version 69, Copyright (C) year name of author
317 317 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 318 This is free software, and you are welcome to redistribute it
319 319 under certain conditions; type `show c' for details.
320 320
321 321 The hypothetical commands `show w' and `show c' should show the appropriate
322 322 parts of the General Public License. Of course, the commands you use may
323 323 be called something other than `show w' and `show c'; they could even be
324 324 mouse-clicks or menu items--whatever suits your program.
325 325
326 326 You should also get your employer (if you work as a programmer) or your
327 327 school, if any, to sign a "copyright disclaimer" for the program, if
328 328 necessary. Here is a sample; alter the names:
329 329
330 330 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 331 `Gnomovision' (which makes passes at compilers) written by James Hacker.
332 332
333 333 <signature of Ty Coon>, 1 April 1989
334 334 Ty Coon, President of Vice
335 335
336 336 This General Public License does not permit incorporating your program into
337 337 proprietary programs. If your program is a subroutine library, you may
338 338 consider it more useful to permit linking proprietary applications with the
339 339 library. If this is what you want to do, use the GNU Library General
340 340 Public License instead of this License.
@@ -1,13 +1,35 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Hg app, a web based mercurial repository managment based on pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
1 21 """
22 Created on April 9, 2010
2 23 Hg app, a web based mercurial repository managment based on pylons
24 @author: marcink
3 25 """
4 26
5 27 VERSION = (0, 7, 6, 'beta')
6 28
7 29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
8 30
9 31 def get_version():
10 32 """
11 33 Returns shorter version (digit parts only) as string.
12 34 """
13 35 return '.'.join((str(each) for each in VERSION[:3]))
@@ -1,70 +1,68 b''
1 1 """Pylons environment configuration"""
2 2 from mako.lookup import TemplateLookup
3 3 from pylons.configuration import PylonsConfig
4 4 from pylons.error import handle_mako_error
5 5 from pylons_app.config.routing import make_map
6 6 from pylons_app.lib.auth import set_available_permissions
7 from pylons_app.lib.utils import repo2db_mapper
7 8 from pylons_app.model import init_model
8 9 from sqlalchemy import engine_from_config
9 10 import logging
10 11 import os
11 12 import pylons_app.lib.app_globals as app_globals
12 13 import pylons_app.lib.helpers
13 14
14
15
16 15 log = logging.getLogger(__name__)
17 16
18 17 def load_environment(global_conf, app_conf):
19 18 """Configure the Pylons environment via the ``pylons.config``
20 19 object
21 20 """
22 21 config = PylonsConfig()
23 22
24 23 # Pylons paths
25 24 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26 25 paths = dict(root=root,
27 26 controllers=os.path.join(root, 'controllers'),
28 27 static_files=os.path.join(root, 'public'),
29 28 templates=[os.path.join(root, 'templates')])
30 29
31 30 # Initialize config with the basic options
32 31 config.init_app(global_conf, app_conf, package='pylons_app', paths=paths)
33 32
34 33 config['routes.map'] = make_map(config)
35 34 config['pylons.app_globals'] = app_globals.Globals(config)
36 35 config['pylons.h'] = pylons_app.lib.helpers
37 36
38 37 # Setup cache object as early as possible
39 38 import pylons
40 39 pylons.cache._push_object(config['pylons.app_globals'].cache)
41 40
42
43 41 # Create the Mako TemplateLookup, with the default auto-escaping
44 42 config['pylons.app_globals'].mako_lookup = TemplateLookup(
45 43 directories=paths['templates'],
46 44 error_handler=handle_mako_error,
47 45 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
48 46 input_encoding='utf-8', default_filters=['escape'],
49 47 imports=['from webhelpers.html import escape'])
50 48
51 #sets the c attribute access when don't existing attribute ar accessed
49 #sets the c attribute access when don't existing attribute are accessed
52 50 config['pylons.strict_tmpl_context'] = True
53 51
54 52 #MULTIPLE DB configs
55 53 # Setup the SQLAlchemy database engine
56 54 if config['debug']:
57 55 #use query time debugging.
58 56 from pylons_app.lib.timerproxy import TimerProxy
59 57 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
60 58 proxy=TimerProxy())
61 59 else:
62 60 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
63 61
64 62 init_model(sa_engine_db1)
65
63 repo2db_mapper()
66 64 set_available_permissions(config)
67 65 # CONFIGURATION OPTIONS HERE (note: all config options will override
68 66 # any Pylons config options)
69 67
70 68 return config
@@ -1,31 +1,54 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # admin controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 7, 2010
22 admin controller for pylons
23 @author: marcink
24 """
1 25 import logging
2 from pylons import request, response, session, tmpl_context as c, url, app_globals as g
3 from pylons.controllers.util import abort, redirect
26 from pylons import request, response, session, tmpl_context as c
4 27 from pylons_app.lib.base import BaseController, render
5 28 from pylons_app.model import meta
6 29 from pylons_app.model.db import UserLog
7 30 from webhelpers.paginate import Page
8 31 from pylons_app.lib.auth import LoginRequired
9 32
10 33 log = logging.getLogger(__name__)
11 34
12 35 class AdminController(BaseController):
13 36
14 37 @LoginRequired()
15 38 def __before__(self):
16 39 user = session['hg_app_user']
17 40 c.admin_user = user.is_admin
18 41 c.admin_username = user.username
19 42 super(AdminController, self).__before__()
20 43
21 44 def index(self):
22 45 sa = meta.Session
23 46
24 47 users_log = sa.query(UserLog).order_by(UserLog.action_date.desc())
25 48 p = int(request.params.get('page', 1))
26 49 c.users_log = Page(users_log, page=p, items_per_page=10)
27 50 c.log_data = render('admin/admin_log.html')
28 51 if request.params.get('partial'):
29 52 return c.log_data
30 53 return render('admin/admin.html')
31 54
@@ -1,20 +1,44 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # branches controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 branches controller for pylons
23 @author: marcink
24 """
1 25 from pylons import tmpl_context as c, app_globals as g
2 26 from pylons_app.lib.auth import LoginRequired
3 27 from pylons_app.lib.base import BaseController, render
4 28 from pylons_app.model.hg_model import HgModel
5 29 import logging
6 30
7 31 log = logging.getLogger(__name__)
8 32
9 33 class BranchesController(BaseController):
10 34
11 35 @LoginRequired()
12 36 def __before__(self):
13 37 super(BranchesController, self).__before__()
14 38
15 39 def index(self):
16 40 hg_model = HgModel()
17 41 c.repo_info = hg_model.get_repo(c.repo_name)
18 42 c.repo_branches = c.repo_info.branches
19 43
20 44 return render('branches/branches.html')
@@ -1,74 +1,95 b''
1 from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET
2 from mercurial.node import short
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # changelog controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 changelog controller for pylons
23 @author: marcink
24 """
3 25 from pylons import request, session, tmpl_context as c
4 26 from pylons_app.lib.auth import LoginRequired
5 27 from pylons_app.lib.base import BaseController, render
6 from pylons_app.lib.filters import age as _age, person
7 28 from pylons_app.model.hg_model import _full_changelog_cached
8 from simplejson import dumps
9 29 from webhelpers.paginate import Page
10 30 import logging
11 31 log = logging.getLogger(__name__)
12 32
13 33 class ChangelogController(BaseController):
14 34
15 35 @LoginRequired()
16 36 def __before__(self):
17 37 super(ChangelogController, self).__before__()
18 38
19 39 def index(self):
20 40 limit = 100
21 41 default = 20
22 42 if request.params.get('size'):
23 43 try:
24 44 int_size = int(request.params.get('size'))
25 45 except ValueError:
26 46 int_size = default
27 47 int_size = int_size if int_size <= limit else limit
28 48 c.size = int_size
29 49 session['changelog_size'] = c.size
30 50 session.save()
31 51 else:
32 52 c.size = session.get('changelog_size', default)
33 53
34 54 changesets = _full_changelog_cached(c.repo_name)
35 55
36 56 p = int(request.params.get('page', 1))
37 57 c.pagination = Page(changesets, page=p, item_count=len(changesets),
38 58 items_per_page=c.size)
39 59
40 60 #self._graph(c.repo, c.size,p)
41 61
42 62 return render('changelog/changelog.html')
43 63
44 64
45 65 def _graph(self, repo, size, p):
46 revcount = size
47 if not repo.revisions:return dumps([]), 0
48
49 max_rev = repo.revisions[-1]
50 offset = 1 if p == 1 else ((p - 1) * revcount)
51 rev_start = repo.revisions[(-1 * offset)]
52 c.bg_height = 120
53
54 revcount = min(max_rev, revcount)
55 rev_end = max(0, rev_start - revcount)
56 dag = graph_rev(repo.repo, rev_start, rev_end)
66 pass
67 # revcount = size
68 # if not repo.revisions:return dumps([]), 0
69 #
70 # max_rev = repo.revisions[-1]
71 # offset = 1 if p == 1 else ((p - 1) * revcount)
72 # rev_start = repo.revisions[(-1 * offset)]
73 # c.bg_height = 120
74 #
75 # revcount = min(max_rev, revcount)
76 # rev_end = max(0, rev_start - revcount)
77 # dag = graph_rev(repo.repo, rev_start, rev_end)
78 #
79 # c.dag = tree = list(colored(dag))
80 # canvasheight = (len(tree) + 1) * c.bg_height - 27
81 # data = []
82 # for (id, type, ctx, vtx, edges) in tree:
83 # if type != CHANGESET:
84 # continue
85 # node = short(ctx.node())
86 # age = _age(ctx.date())
87 # desc = ctx.description()
88 # user = person(ctx.user())
89 # branch = ctx.branch()
90 # branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
91 # data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
92 #
93 # c.jsdata = dumps(data)
94 # c.canvasheight = canvasheight
57 95
58 c.dag = tree = list(colored(dag))
59 canvasheight = (len(tree) + 1) * c.bg_height - 27
60 data = []
61 for (id, type, ctx, vtx, edges) in tree:
62 if type != CHANGESET:
63 continue
64 node = short(ctx.node())
65 age = _age(ctx.date())
66 desc = ctx.description()
67 user = person(ctx.user())
68 branch = ctx.branch()
69 branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
70 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
71
72 c.jsdata = dumps(data)
73 c.canvasheight = canvasheight
74
@@ -1,40 +1,64 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # changeset controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 25, 2010
22 changeset controller for pylons
23 @author: marcink
24 """
1 25 from pylons import tmpl_context as c
2 26 from pylons_app.lib.auth import LoginRequired
3 27 from pylons_app.lib.base import BaseController, render
4 28 from pylons_app.model.hg_model import HgModel
5 29 from vcs.utils import diffs as differ
6 30 import logging
7 31 from vcs.nodes import FileNode
8 32
9 33
10 34 log = logging.getLogger(__name__)
11 35
12 36 class ChangesetController(BaseController):
13 37
14 38 @LoginRequired()
15 39 def __before__(self):
16 40 super(ChangesetController, self).__before__()
17 41
18 42 def index(self, revision):
19 43 hg_model = HgModel()
20 44 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
21 45 c.changeset_old = c.changeset.parents[0]
22 46 c.changes = []
23 47
24 48
25 49 for node in c.changeset.added:
26 50 filenode_old = FileNode(node.path, '')
27 51 f_udiff = differ.get_udiff(filenode_old, node)
28 52 diff = differ.DiffProcessor(f_udiff).as_html()
29 53 c.changes.append(('added', node, diff))
30 54
31 55 for node in c.changeset.changed:
32 56 filenode_old = c.changeset_old.get_node(node.path)
33 57 f_udiff = differ.get_udiff(filenode_old, node)
34 58 diff = differ.DiffProcessor(f_udiff).as_html()
35 59 c.changes.append(('changed', node, diff))
36 60
37 61 for node in c.changeset.removed:
38 62 c.changes.append(('removed', node, None))
39 63
40 64 return render('changeset/changeset.html')
@@ -1,59 +1,81 b''
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # feed controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 23, 2010
22 feed controller for pylons
23 @author: marcink
24 """
3 25 from pylons import tmpl_context as c, url, response
4 26 from pylons_app.lib.base import BaseController, render
5 27 from pylons_app.model.hg_model import _full_changelog_cached
6 28 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
7 29 import logging
8 30 log = logging.getLogger(__name__)
9 31
10 32 class FeedController(BaseController):
11 33
12 34 #secure it or not ?
13 35 def __before__(self):
14 36 super(FeedController, self).__before__()
15 37 #common values for feeds
16 38 self.description = 'Changes on %s repository'
17 39 self.title = "%s feed"
18 40 self.language = 'en-us'
19 41 self.ttl = "5"
20 42 self.feed_nr = 10
21 43
22 44 def atom(self, repo_name):
23 45 """Produce an atom-1.0 feed via feedgenerator module"""
24 46 feed = Atom1Feed(title=self.title % repo_name,
25 47 link=url('summary_home', repo_name=repo_name, qualified=True),
26 48 description=self.description % repo_name,
27 49 language=self.language,
28 50 ttl=self.ttl)
29 51
30 52
31 53 for cnt, cs in enumerate(_full_changelog_cached(repo_name)):
32 54 if cnt > self.feed_nr:
33 55 break
34 56 feed.add_item(title=cs.message,
35 57 link=url('changeset_home', repo_name=repo_name,
36 58 revision=cs.raw_id, qualified=True),
37 59 description=str(cs.date))
38 60
39 61 response.content_type = feed.mime_type
40 62 return feed.writeString('utf-8')
41 63
42 64
43 65 def rss(self, repo_name):
44 66 """Produce an rss2 feed via feedgenerator module"""
45 67 feed = Rss201rev2Feed(title=self.title % repo_name,
46 68 link=url('summary_home', repo_name=repo_name, qualified=True),
47 69 description=self.description % repo_name,
48 70 language=self.language,
49 71 ttl=self.ttl)
50 72
51 73 for cnt, cs in enumerate(_full_changelog_cached(repo_name)):
52 74 if cnt > self.feed_nr:
53 75 break
54 76 feed.add_item(title=cs.message,
55 77 link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True),
56 78 description=str(cs.date))
57 79
58 80 response.content_type = feed.mime_type
59 81 return feed.writeString('utf-8')
@@ -1,154 +1,178 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # files controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 files controller for pylons
23 @author: marcink
24 """
1 25 from mercurial import archival
2 26 from pylons import request, response, session, tmpl_context as c, url
3 27 from pylons_app.lib.auth import LoginRequired
4 28 from pylons_app.lib.base import BaseController, render
5 29 from pylons_app.model.hg_model import HgModel
6 30 from vcs.exceptions import RepositoryError, ChangesetError
7 31 from vcs.utils import diffs as differ
8 32 import logging
9 33 import tempfile
10 34
11 35
12 36 log = logging.getLogger(__name__)
13 37
14 38 class FilesController(BaseController):
15 39
16 40 @LoginRequired()
17 41 def __before__(self):
18 42 super(FilesController, self).__before__()
19 43
20 44 def index(self, repo_name, revision, f_path):
21 45 hg_model = HgModel()
22 46 c.repo = repo = hg_model.get_repo(c.repo_name)
23 47 revision = request.POST.get('at_rev', None) or revision
24 48
25 49 def get_next_rev(cur):
26 50 max_rev = len(c.repo.revisions) - 1
27 51 r = cur + 1
28 52 if r > max_rev:
29 53 r = max_rev
30 54 return r
31 55
32 56 def get_prev_rev(cur):
33 57 r = cur - 1
34 58 return r
35 59
36 60 c.f_path = f_path
37 61
38 62
39 63 try:
40 64 cur_rev = repo.get_changeset(revision).revision
41 65 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
42 66 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
43 67
44 68 c.url_prev = url('files_home', repo_name=c.repo_name,
45 69 revision=prev_rev, f_path=f_path)
46 70 c.url_next = url('files_home', repo_name=c.repo_name,
47 71 revision=next_rev, f_path=f_path)
48 72
49 73 c.changeset = repo.get_changeset(revision)
50 74 try:
51 75 c.file_msg = c.changeset.get_file_message(f_path)
52 76 except:
53 77 c.file_msg = None
54 78
55 79 c.cur_rev = c.changeset.raw_id
56 80 c.rev_nr = c.changeset.revision
57 81 c.files_list = c.changeset.get_node(f_path)
58 82 c.file_history = self._get_history(repo, c.files_list, f_path)
59 83
60 84 except (RepositoryError, ChangesetError):
61 85 c.files_list = None
62 86
63 87 return render('files/files.html')
64 88
65 89 def rawfile(self, repo_name, revision, f_path):
66 90 hg_model = HgModel()
67 91 c.repo = hg_model.get_repo(c.repo_name)
68 92 file_node = c.repo.get_changeset(revision).get_node(f_path)
69 93 response.content_type = file_node.mimetype
70 94 response.content_disposition = 'attachment; filename=%s' \
71 95 % f_path.split('/')[-1]
72 96 return file_node.content
73 97
74 98 def annotate(self, repo_name, revision, f_path):
75 99 hg_model = HgModel()
76 100 c.repo = hg_model.get_repo(c.repo_name)
77 101 cs = c.repo.get_changeset(revision)
78 102 c.file = cs.get_node(f_path)
79 103 c.file_msg = cs.get_file_message(f_path)
80 104 c.cur_rev = cs.raw_id
81 105 c.f_path = f_path
82 106 c.annotate = cs.get_file_annotate(f_path)
83 107 return render('files/files_annotate.html')
84 108
85 109 def archivefile(self, repo_name, revision, fileformat):
86 110 archive_specs = {
87 111 '.tar.bz2': ('application/x-tar', 'tbz2'),
88 112 '.tar.gz': ('application/x-tar', 'tgz'),
89 113 '.zip': ('application/zip', 'zip'),
90 114 }
91 115 if not archive_specs.has_key(fileformat):
92 116 return 'Unknown archive type %s' % fileformat
93 117
94 118 def read_in_chunks(file_object, chunk_size=1024 * 40):
95 119 """Lazy function (generator) to read a file piece by piece.
96 120 Default chunk size: 40k."""
97 121 while True:
98 122 data = file_object.read(chunk_size)
99 123 if not data:
100 124 break
101 125 yield data
102 126
103 127 archive = tempfile.TemporaryFile()
104 128 repo = HgModel().get_repo(repo_name).repo
105 129 fname = '%s-%s%s' % (repo_name, revision, fileformat)
106 130 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
107 131 prefix='%s-%s' % (repo_name, revision))
108 132 response.content_type = archive_specs[fileformat][0]
109 133 response.content_disposition = 'attachment; filename=%s' % fname
110 134 archive.seek(0)
111 135 return read_in_chunks(archive)
112 136
113 137 def diff(self, repo_name, f_path):
114 138 hg_model = HgModel()
115 139 diff1 = request.GET.get('diff1')
116 140 diff2 = request.GET.get('diff2')
117 141 c.action = action = request.GET.get('diff')
118 142 c.no_changes = diff1 == diff2
119 143 c.f_path = f_path
120 144 c.repo = hg_model.get_repo(c.repo_name)
121 145 c.changeset_1 = c.repo.get_changeset(diff1)
122 146 c.changeset_2 = c.repo.get_changeset(diff2)
123 147
124 148 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short)
125 149 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short)
126 150 f_udiff = differ.get_udiff(c.changeset_1.get_node(f_path),
127 151 c.changeset_2.get_node(f_path))
128 152
129 153 diff = differ.DiffProcessor(f_udiff)
130 154
131 155 if action == 'download':
132 156 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
133 157 response.content_type = 'text/plain'
134 158 response.content_disposition = 'attachment; filename=%s' \
135 159 % diff_name
136 160 return diff.raw_diff()
137 161
138 162 elif action == 'raw':
139 163 c.cur_diff = '<pre class="raw">%s</pre>' % diff.raw_diff()
140 164 elif action == 'diff':
141 165 c.cur_diff = diff.as_html()
142 166
143 167 return render('files/file_diff.html')
144 168
145 169 def _get_history(self, repo, node, f_path):
146 170 from vcs.nodes import NodeKind
147 171 if not node.kind is NodeKind.FILE:
148 172 return []
149 173 changesets = node.history
150 174 hist_l = []
151 175 for chs in changesets:
152 176 n_desc = 'r%s:%s' % (chs.revision, chs._short)
153 177 hist_l.append((chs._short, n_desc,))
154 178 return hist_l
@@ -1,66 +1,84 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # graph controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 graph controller for pylons
23 @author: marcink
24 """
1 25 from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET
2 26 from mercurial.node import short
3 from pylons import request, response, session, tmpl_context as c, url, config, \
4 app_globals as g
5 from pylons.controllers.util import abort, redirect
27 from pylons import request, tmpl_context as c
6 28 from pylons_app.lib.auth import LoginRequired
7 29 from pylons_app.lib.base import BaseController, render
8 30 from pylons_app.lib.filters import age as _age, person
9 from pylons_app.lib.utils import get_repo_slug
10 31 from pylons_app.model.hg_model import HgModel
11 32 from simplejson import dumps
12 33 from webhelpers.paginate import Page
13 34 import logging
14 35
15
16
17
18 36 log = logging.getLogger(__name__)
19 37
20 38 class GraphController(BaseController):
21 39
22 40 @LoginRequired()
23 41 def __before__(self):
24 42 super(GraphController, self).__before__()
25 43
26 44 def index(self):
27 45 # Return a rendered template
28 46 hg_model = HgModel()
29 47 if request.POST.get('size'):
30 48 c.size = int(request.params.get('size', 20))
31 49 else:
32 50 c.size = int(request.params.get('size', 20))
33 51 c.jsdata, c.canvasheight = self.graph(hg_model.get_repo(c.repo_name), c.size)
34 52
35 53 return render('/graph.html')
36 54
37 55
38 56 def graph(self, repo, size):
39 57 revcount = size
40 58 p = int(request.params.get('page', 1))
41 59 c.pagination = Page(repo.revisions, page=p, item_count=len(repo.revisions), items_per_page=revcount)
42 60 if not repo.revisions:return dumps([]), 0
43 61
44 62 max_rev = repo.revisions[-1]
45 63 offset = 1 if p == 1 else ((p - 1) * revcount)
46 64 rev_start = repo.revisions[(-1 * offset)]
47 65 bg_height = 39
48 66
49 67 revcount = min(max_rev, revcount)
50 68 rev_end = max(0, rev_start - revcount)
51 69 dag = graph_rev(repo.repo, rev_start, rev_end)
52 70 tree = list(colored(dag))
53 71 canvasheight = (len(tree) + 1) * bg_height - 27
54 72 data = []
55 73 for (id, type, ctx, vtx, edges) in tree:
56 74 if type != CHANGESET:
57 75 continue
58 76 node = short(ctx.node())
59 77 age = _age(ctx.date())
60 78 desc = ctx.description()
61 79 user = person(ctx.user())
62 80 branch = ctx.branch()
63 81 branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
64 82 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
65 83
66 84 return dumps(data), canvasheight
@@ -1,30 +1,52 b''
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # hg controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on February 18, 2010
22 hg controller for pylons
23 @author: marcink
24 """
3 25 from operator import itemgetter
4 from pylons import tmpl_context as c, request, config
26 from pylons import tmpl_context as c, request
5 27 from pylons_app.lib.auth import LoginRequired
6 28 from pylons_app.lib.base import BaseController, render
7 29 from pylons_app.model.hg_model import HgModel
8 30 import logging
9 31 log = logging.getLogger(__name__)
10 32
11 33 class HgController(BaseController):
12 34
13 35 @LoginRequired()
14 36 def __before__(self):
15 37 super(HgController, self).__before__()
16 38
17 39 def index(self):
18 40 c.current_sort = request.GET.get('sort', 'name')
19 41 cs = c.current_sort
20 42 c.cs_slug = cs.replace('-', '')
21 43 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
22 44 cached_repo_list = HgModel().get_repos()
23 45 if cs and c.cs_slug in sortables:
24 46 sort_key = c.cs_slug + '_sort'
25 47 if cs.startswith('-'):
26 48 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True)
27 49 else:
28 50 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False)
29 51
30 52 return render('/index.html')
@@ -1,42 +1,66 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # login controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 22, 2010
22 login controller for pylons
23 @author: marcink
24 """
1 25 import logging
2 26 from formencode import htmlfill
3 27 from pylons import request, response, session, tmpl_context as c, url
4 28 from pylons.controllers.util import abort, redirect
5 29 from pylons_app.lib.base import BaseController, render
6 30 import formencode
7 31 from pylons_app.model.forms import LoginForm
8 32 from pylons_app.lib.auth import AuthUser
9 33
10 34 log = logging.getLogger(__name__)
11 35
12 36 class LoginController(BaseController):
13 37
14 38 def __before__(self):
15 39 super(LoginController, self).__before__()
16 40
17 41 def index(self):
18 42 #redirect if already logged in
19 43 if c.hg_app_user.is_authenticated:
20 44 return redirect(url('hg_home'))
21 45
22 46 if request.POST:
23 47 #import Login Form validator class
24 48 login_form = LoginForm()
25 49 try:
26 50 c.form_result = login_form.to_python(dict(request.POST))
27 51 return redirect(url('hg_home'))
28 52
29 53 except formencode.Invalid as errors:
30 54 c.form_errors = errors.error_dict
31 55 return htmlfill.render(
32 56 render('/login.html'),
33 57 defaults=errors.value,
34 58 encoding="UTF-8")
35 59
36 60 return render('/login.html')
37 61
38 62 def logout(self):
39 63 session['hg_app_user'] = AuthUser()
40 64 session.save()
41 65 log.info('Logging out and setting user as Empty')
42 66 redirect(url('hg_home'))
@@ -1,53 +1,77 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # permissions controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 27, 2010
22 permissions controller for pylons
23 @author: marcink
24 """
1 25 import logging
2 26
3 27 from pylons import request, response, session, tmpl_context as c, url
4 28 from pylons.controllers.util import abort, redirect
5 29
6 30 from pylons_app.lib.base import BaseController, render
7 31
8 32 log = logging.getLogger(__name__)
9 33
10 34 class PermissionsController(BaseController):
11 35 """REST Controller styled on the Atom Publishing Protocol"""
12 36 # To properly map this controller, ensure your config/routing.py
13 37 # file has a resource setup:
14 38 # map.resource('permission', 'permissions')
15 39
16 40 def index(self, format='html'):
17 41 """GET /permissions: All items in the collection"""
18 42 # url('permissions')
19 43 return render('admin/permissions/permissions.html')
20 44
21 45 def create(self):
22 46 """POST /permissions: Create a new item"""
23 47 # url('permissions')
24 48
25 49 def new(self, format='html'):
26 50 """GET /permissions/new: Form to create a new item"""
27 51 # url('new_permission')
28 52
29 53 def update(self, id):
30 54 """PUT /permissions/id: Update an existing item"""
31 55 # Forms posted to this method should contain a hidden field:
32 56 # <input type="hidden" name="_method" value="PUT" />
33 57 # Or using helpers:
34 58 # h.form(url('permission', id=ID),
35 59 # method='put')
36 60 # url('permission', id=ID)
37 61
38 62 def delete(self, id):
39 63 """DELETE /permissions/id: Delete an existing item"""
40 64 # Forms posted to this method should contain a hidden field:
41 65 # <input type="hidden" name="_method" value="DELETE" />
42 66 # Or using helpers:
43 67 # h.form(url('permission', id=ID),
44 68 # method='delete')
45 69 # url('permission', id=ID)
46 70
47 71 def show(self, id, format='html'):
48 72 """GET /permissions/id: Show a specific item"""
49 73 # url('permission', id=ID)
50 74
51 75 def edit(self, id, format='html'):
52 76 """GET /permissions/id/edit: Form to edit an existing item"""
53 77 # url('edit_permission', id=ID)
@@ -1,103 +1,127 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # repos controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 7, 2010
22 admin controller for pylons
23 @author: marcink
24 """
25 import logging
1 26 from pylons import request, response, session, tmpl_context as c, url, \
2 27 app_globals as g
3 28 from pylons.controllers.util import abort, redirect
4 29 from pylons_app.lib.auth import LoginRequired
5 30 from pylons.i18n.translation import _
6 31 from pylons_app.lib import helpers as h
7 32 from pylons_app.lib.base import BaseController, render
8 33 from pylons_app.lib.filters import clean_repo
9 34 from pylons_app.lib.utils import check_repo, invalidate_cache
10 35 from pylons_app.model.hg_model import HgModel
11 import logging
12 36 import os
13 37 import shutil
14 38 from operator import itemgetter
15 39 log = logging.getLogger(__name__)
16 40
17 41 class ReposController(BaseController):
18 42 """REST Controller styled on the Atom Publishing Protocol"""
19 43 # To properly map this controller, ensure your config/routing.py
20 44 # file has a resource setup:
21 45 # map.resource('repo', 'repos')
22 46 @LoginRequired()
23 47 def __before__(self):
24 48 c.admin_user = session.get('admin_user')
25 49 c.admin_username = session.get('admin_username')
26 50 super(ReposController, self).__before__()
27 51
28 52 def index(self, format='html'):
29 53 """GET /repos: All items in the collection"""
30 54 # url('repos')
31 55 cached_repo_list = HgModel().get_repos()
32 56 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
33 57 return render('admin/repos/repos.html')
34 58
35 59 def create(self):
36 60 """POST /repos: Create a new item"""
37 61 # url('repos')
38 62 name = request.POST.get('name')
39 63
40 64 try:
41 65 self._create_repo(name)
42 66 #clear our cached list for refresh with new repo
43 67 invalidate_cache('cached_repo_list')
44 68 h.flash(_('created repository %s') % name, category='success')
45 69 except Exception as e:
46 70 log.error(e)
47 71
48 72 return redirect('repos')
49 73
50 74 def _create_repo(self, repo_name):
51 75 repo_path = os.path.join(g.base_path, repo_name)
52 76 if check_repo(repo_name, g.base_path):
53 77 log.info('creating repo %s in %s', repo_name, repo_path)
54 78 from vcs.backends.hg import MercurialRepository
55 79 MercurialRepository(repo_path, create=True)
56 80
57 81
58 82 def new(self, format='html'):
59 83 """GET /repos/new: Form to create a new item"""
60 84 new_repo = request.GET.get('repo', '')
61 85 c.new_repo = clean_repo(new_repo)
62 86
63 87 return render('admin/repos/repo_add.html')
64 88
65 89 def update(self, id):
66 90 """PUT /repos/id: Update an existing item"""
67 91 # Forms posted to this method should contain a hidden field:
68 92 # <input type="hidden" name="_method" value="PUT" />
69 93 # Or using helpers:
70 94 # h.form(url('repo', id=ID),
71 95 # method='put')
72 96 # url('repo', id=ID)
73 97
74 98 def delete(self, id):
75 99 """DELETE /repos/id: Delete an existing item"""
76 100 # Forms posted to this method should contain a hidden field:
77 101 # <input type="hidden" name="_method" value="DELETE" />
78 102 # Or using helpers:
79 103 # h.form(url('repo', id=ID),
80 104 # method='delete')
81 105 # url('repo', id=ID)
82 106 from datetime import datetime
83 107 path = g.paths[0][1].replace('*', '')
84 108 rm_path = os.path.join(path, id)
85 109 log.info("Removing %s", rm_path)
86 110 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
87 111 shutil.move(rm_path, os.path.join(path, 'rm__%s-%s' % (datetime.today(), id)))
88 112
89 113 #clear our cached list for refresh with new repo
90 114 invalidate_cache('cached_repo_list')
91 115 h.flash(_('deleted repository %s') % rm_path, category='success')
92 116 return redirect(url('repos'))
93 117
94 118
95 119 def show(self, id, format='html'):
96 120 """GET /repos/id: Show a specific item"""
97 121 # url('repo', id=ID)
98 122
99 123 def edit(self, id, format='html'):
100 124 """GET /repos/id/edit: Form to edit an existing item"""
101 125 # url('edit_repo', id=ID)
102 126 c.new_repo = id
103 127 return render('admin/repos/repo_edit.html')
@@ -1,25 +1,49 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # shortlog controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 18, 2010
22 shortlog controller for pylons
23 @author: marcink
24 """
1 25 from pylons import tmpl_context as c, request
2 26 from pylons_app.lib.auth import LoginRequired
3 27 from pylons_app.lib.base import BaseController, render
4 28 from pylons_app.model.hg_model import HgModel
5 29 from webhelpers.paginate import Page
6 30 import logging
7 31
8 32 log = logging.getLogger(__name__)
9 33
10 34 class ShortlogController(BaseController):
11 35
12 36 @LoginRequired()
13 37 def __before__(self):
14 38 super(ShortlogController, self).__before__()
15 39
16 40 def index(self):
17 41 hg_model = HgModel()
18 42 p = int(request.params.get('page', 1))
19 43 repo = hg_model.get_repo(c.repo_name)
20 44 c.repo_changesets = Page(repo, page=p, items_per_page=20)
21 45 c.shortlog_data = render('shortlog/shortlog_data.html')
22 46 if request.params.get('partial'):
23 47 return c.shortlog_data
24 48 r = render('shortlog/shortlog.html')
25 49 return r
@@ -1,28 +1,52 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # summary controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 18, 2010
22 summary controller for pylons
23 @author: marcink
24 """
1 25 from pylons import tmpl_context as c, request
2 26 from pylons_app.lib.auth import LoginRequired
3 27 from pylons_app.lib.base import BaseController, render
4 28 from pylons_app.model.hg_model import HgModel, _full_changelog_cached
5 29 import logging
6 30
7 31 log = logging.getLogger(__name__)
8 32
9 33 class SummaryController(BaseController):
10 34
11 35 @LoginRequired()
12 36 def __before__(self):
13 37 super(SummaryController, self).__before__()
14 38
15 39 def index(self):
16 40 hg_model = HgModel()
17 41 c.repo_info = hg_model.get_repo(c.repo_name)
18 42 c.repo_changesets = _full_changelog_cached(c.repo_name)[:10]
19 43 e = request.environ
20 44 uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
21 45 'protocol': e.get('wsgi.url_scheme'),
22 46 'user':str(c.hg_app_user.username),
23 47 'host':e.get('HTTP_HOST'),
24 48 'repo_name':c.repo_name, }
25 49 c.clone_repo_url = uri
26 50 c.repo_tags = c.repo_info.tags[:10]
27 51 c.repo_branches = c.repo_info.branches[:10]
28 52 return render('summary/summary.html')
@@ -1,20 +1,44 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # tags controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 tags controller for pylons
23 @author: marcink
24 """
1 25 from pylons import tmpl_context as c
2 26 from pylons_app.lib.auth import LoginRequired
3 27 from pylons_app.lib.base import BaseController, render
4 28 from pylons_app.model.hg_model import HgModel
5 29 import logging
6 30
7 31 log = logging.getLogger(__name__)
8 32
9 33 class TagsController(BaseController):
10 34
11 35 @LoginRequired()
12 36 def __before__(self):
13 37 super(TagsController, self).__before__()
14 38
15 39 def index(self):
16 40 hg_model = HgModel()
17 41 c.repo_info = hg_model.get_repo(c.repo_name)
18 42 c.repo_tags = c.repo_info.tags
19 43
20 44 return render('tags/tags.html')
@@ -1,115 +1,139 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # users controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 4, 2010
22 users controller for pylons
23 @author: marcink
24 """
25 import logging
1 26 from formencode import htmlfill
2 27 from pylons import request, session, tmpl_context as c, url
3 28 from pylons.controllers.util import abort, redirect
4 29 from pylons.i18n.translation import _
5 30 from pylons_app.lib import helpers as h
6 31 from pylons_app.lib.auth import LoginRequired, CheckPermissionAll
7 32 from pylons_app.lib.base import BaseController, render
8 33 from pylons_app.model.db import User, UserLog
9 34 from pylons_app.model.forms import UserForm
10 35 from pylons_app.model.user_model import UserModel
11 36 import formencode
12 import logging
13 37
14 38 log = logging.getLogger(__name__)
15 39
16 40 class UsersController(BaseController):
17 41 """REST Controller styled on the Atom Publishing Protocol"""
18 42 # To properly map this controller, ensure your config/routing.py
19 43 # file has a resource setup:
20 44 # map.resource('user', 'users')
21 45 @LoginRequired()
22 46 def __before__(self):
23 47 c.admin_user = session.get('admin_user')
24 48 c.admin_username = session.get('admin_username')
25 49 super(UsersController, self).__before__()
26 50
27 51
28 52 def index(self, format='html'):
29 53 """GET /users: All items in the collection"""
30 54 # url('users')
31 55
32 56 c.users_list = self.sa.query(User).all()
33 57 return render('admin/users/users.html')
34 58
35 59 def create(self):
36 60 """POST /users: Create a new item"""
37 61 # url('users')
38 62
39 63 user_model = UserModel()
40 64 login_form = UserForm()()
41 65 try:
42 66 form_result = login_form.to_python(dict(request.POST))
43 67 user_model.create(form_result)
44 68 h.flash(_('created user %s') % form_result['username'], category='success')
45 69 return redirect(url('users'))
46 70
47 71 except formencode.Invalid as errors:
48 72 c.form_errors = errors.error_dict
49 73 return htmlfill.render(
50 74 render('admin/users/user_add.html'),
51 75 defaults=errors.value,
52 76 encoding="UTF-8")
53 77
54 78 def new(self, format='html'):
55 79 """GET /users/new: Form to create a new item"""
56 80 # url('new_user')
57 81 return render('admin/users/user_add.html')
58 82
59 83 def update(self, id):
60 84 """PUT /users/id: Update an existing item"""
61 85 # Forms posted to this method should contain a hidden field:
62 86 # <input type="hidden" name="_method" value="PUT" />
63 87 # Or using helpers:
64 88 # h.form(url('user', id=ID),
65 89 # method='put')
66 90 # url('user', id=ID)
67 91 user_model = UserModel()
68 92 login_form = UserForm(edit=True)()
69 93 try:
70 94 form_result = login_form.to_python(dict(request.POST))
71 95 user_model.update(id, form_result)
72 96 h.flash(_('User updated succesfully'), category='success')
73 97 return redirect(url('users'))
74 98
75 99 except formencode.Invalid as errors:
76 100 c.user = user_model.get_user(id)
77 101 c.form_errors = errors.error_dict
78 102 return htmlfill.render(
79 103 render('admin/users/user_edit.html'),
80 104 defaults=errors.value,
81 105 encoding="UTF-8")
82 106
83 107 def delete(self, id):
84 108 """DELETE /users/id: Delete an existing item"""
85 109 # Forms posted to this method should contain a hidden field:
86 110 # <input type="hidden" name="_method" value="DELETE" />
87 111 # Or using helpers:
88 112 # h.form(url('user', id=ID),
89 113 # method='delete')
90 114 # url('user', id=ID)
91 115 try:
92 116 self.sa.delete(self.sa.query(User).get(id))
93 117 self.sa.commit()
94 118 h.flash(_('sucessfully deleted user'), category='success')
95 119 except:
96 120 self.sa.rollback()
97 121 raise
98 122 return redirect(url('users'))
99 123
100 124 def show(self, id, format='html'):
101 125 """GET /users/id: Show a specific item"""
102 126 # url('user', id=ID)
103 127
104 128
105 129 def edit(self, id, format='html'):
106 130 """GET /users/id/edit: Form to edit an existing item"""
107 131 # url('edit_user', id=ID)
108 132 c.user = self.sa.query(User).get(id)
109 133 defaults = c.user.__dict__
110 134 return htmlfill.render(
111 135 render('admin/users/user_edit.html'),
112 136 defaults=defaults,
113 137 encoding="UTF-8",
114 138 force_defaults=False
115 139 )
@@ -1,153 +1,178 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on April 4, 2010
22
23 @author: marcink
24 """
25
1 26 from functools import wraps
2 27 from pylons import session, url, app_globals as g
3 28 from pylons.controllers.util import abort, redirect
4 29 from pylons_app.model import meta
5 30 from pylons_app.model.db import User
6 31 from sqlalchemy.exc import OperationalError
7 32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
8 33 import crypt
9 34 import logging
10 35 log = logging.getLogger(__name__)
11 36
12 37 def get_crypt_password(password):
13 38 """
14 39 Cryptographic function used for password hashing
15 40 @param password: password to hash
16 41 """
17 42 return crypt.crypt(password, '6a')
18 43
19 44 def authfunc(environ, username, password):
20 45 sa = meta.Session
21 46 password_crypt = get_crypt_password(password)
22 47 try:
23 48 user = sa.query(User).filter(User.username == username).one()
24 49 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
25 50 log.error(e)
26 51 user = None
27 52
28 53 if user:
29 54 if user.active:
30 55 if user.username == username and user.password == password_crypt:
31 56 log.info('user %s authenticated correctly', username)
32 57 return True
33 58 else:
34 59 log.error('user %s is disabled', username)
35 60
36 61 return False
37 62
38 63 class AuthUser(object):
39 64 """
40 65 A simple object that handles a mercurial username for authentication
41 66 """
42 67 username = 'None'
43 68 is_authenticated = False
44 69 is_admin = False
45 70 permissions = set()
46 71 group = set()
47 72
48 73 def __init__(self):
49 74 pass
50 75
51 76
52 77
53 78 def set_available_permissions(config):
54 79 """
55 80 This function will propagate pylons globals with all available defined
56 81 permission given in db. We don't wannt to check each time from db for new
57 82 permissions since adding a new permission also requires application restart
58 83 ie. to decorate new views with the newly created permission
59 84 @param config:
60 85 """
61 86 from pylons_app.model.meta import Session
62 87 from pylons_app.model.db import Permission
63 88 logging.info('getting information about all available permissions')
64 89 sa = Session()
65 90 all_perms = sa.query(Permission).all()
66 91 config['pylons.app_globals'].available_permissions = [x.permission_name for x in all_perms]
67 92
68 93
69 94
70 95 #===============================================================================
71 96 # DECORATORS
72 97 #===============================================================================
73 98 class LoginRequired(object):
74 99 """
75 100 Must be logged in to execute this function else redirect to login page
76 101 """
77 102 def __init__(self):
78 103 pass
79 104
80 105 def __call__(self, func):
81 106
82 107 @wraps(func)
83 108 def _wrapper(*fargs, **fkwargs):
84 109 user = session.get('hg_app_user', AuthUser())
85 110 log.info('Checking login required for user:%s', user.username)
86 111 if user.is_authenticated:
87 112 log.info('user %s is authenticated', user.username)
88 113 func(*fargs)
89 114 else:
90 115 logging.info('user %s not authenticated', user.username)
91 116 logging.info('redirecting to login page')
92 117 return redirect(url('login_home'))
93 118
94 119 return _wrapper
95 120
96 121 class PermsDecorator(object):
97 122
98 123 def __init__(self, *perms):
99 124 available_perms = g.available_permissions
100 125 for perm in perms:
101 126 if perm not in available_perms:
102 127 raise Exception("'%s' permission in not defined" % perm)
103 128 self.required_perms = set(perms)
104 129 self.user_perms = set([])#propagate this list from somewhere.
105 130
106 131 def __call__(self, func):
107 132 @wraps(func)
108 133 def _wrapper(*args, **kwargs):
109 134 logging.info('checking %s permissions %s for %s',
110 135 self.__class__.__name__[-3:], self.required_perms, func.__name__)
111 136
112 137 if self.check_permissions():
113 138 logging.info('Permission granted for %s', func.__name__)
114 139 return func(*args, **kwargs)
115 140
116 141 else:
117 142 logging.warning('Permission denied for %s', func.__name__)
118 143 #redirect with forbidden ret code
119 144 return redirect(url('access_denied'), 403)
120 145 return _wrapper
121 146
122 147
123 148 def check_permissions(self):
124 149 """
125 150 Dummy function for overiding
126 151 """
127 152 raise Exception('You have to write this function in child class')
128 153
129 154 class CheckPermissionAll(PermsDecorator):
130 155 """
131 156 Checks for access permission for all given predicates. All of them have to
132 157 be meet in order to fulfill the request
133 158 """
134 159
135 160 def check_permissions(self):
136 161 if self.required_perms.issubset(self.user_perms):
137 162 return True
138 163 return False
139 164
140 165
141 166 class CheckPermissionAny(PermsDecorator):
142 167 """
143 168 Checks for access permission for any of given predicates. In order to
144 169 fulfill the request any of predicates must be meet
145 170 """
146 171
147 172 def check_permissions(self):
148 173 if self.required_perms.intersection(self.user_perms):
149 174 return True
150 175 return False
151 176
152 177
153 178
@@ -1,88 +1,114 b''
1 '''BACKUP MANAGER'''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # mercurial repository backup manager
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 """
22 Created on Feb 28, 2010
23 Mercurial repositories backup manager
24 @author: marcink
25 """
26
27
2 28 import logging
3 29 from mercurial import config
4 30 import tarfile
5 31 import os
6 32 import datetime
7 33 import sys
8 34 import subprocess
9 35 logging.basicConfig(level=logging.DEBUG,
10 36 format="%(asctime)s %(levelname)-5.5s %(message)s")
11 37
12 38 class BackupManager(object):
13 39 def __init__(self, id_rsa_path, repo_conf):
14 40 self.repos_path = None
15 41 self.backup_file_name = None
16 42 self.id_rsa_path = id_rsa_path
17 43 self.check_id_rsa()
18 44 cur_dir = os.path.realpath(__file__)
19 45 dn = os.path.dirname
20 46 self.backup_file_path = os.path.join(dn(dn(dn(cur_dir))), 'data')
21 47 cfg = config.config()
22 48 try:
23 49 cfg.read(os.path.join(dn(dn(dn(cur_dir))), repo_conf))
24 50 except IOError:
25 51 logging.error('Could not read %s', repo_conf)
26 52 sys.exit()
27 53 self.set_repos_path(cfg.items('paths'))
28 54 logging.info('starting backup for %s', self.repos_path)
29 55 logging.info('backup target %s', self.backup_file_path)
30 56
31 57 if not os.path.isdir(self.repos_path):
32 58 raise Exception('Not a valid directory in %s' % self.repos_path)
33 59
34 60 def check_id_rsa(self):
35 61 if not os.path.isfile(self.id_rsa_path):
36 62 logging.error('Could not load id_rsa key file in %s',
37 63 self.id_rsa_path)
38 64 sys.exit()
39 65
40 66 def set_repos_path(self, paths):
41 67 repos_path = paths[0][1].split('/')
42 68 if repos_path[-1] in ['*', '**']:
43 69 repos_path = repos_path[:-1]
44 70 if repos_path[0] != '/':
45 71 repos_path[0] = '/'
46 72 self.repos_path = os.path.join(*repos_path)
47 73
48 74 def backup_repos(self):
49 75 today = datetime.datetime.now().weekday() + 1
50 76 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
51 77 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
52 78 tar = tarfile.open(bckp_file, "w:gz")
53 79
54 80 for dir_name in os.listdir(self.repos_path):
55 81 logging.info('backing up %s', dir_name)
56 82 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
57 83 tar.close()
58 84 logging.info('finished backup of mercurial repositories')
59 85
60 86
61 87
62 88 def transfer_files(self):
63 89 params = {
64 90 'id_rsa_key': self.id_rsa_path,
65 91 'backup_file_path':self.backup_file_path,
66 92 'backup_file_name':self.backup_file_name,
67 93 }
68 94 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
69 95 '%(backup_file_path)s/%(backup_file_name)s' % params,
70 96 'root@192.168.2.102:/backups/mercurial' % params]
71 97
72 98 subprocess.call(cmd)
73 99 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
74 100
75 101
76 102 def rm_file(self):
77 103 logging.info('Removing file %s', self.backup_file_name)
78 104 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
79 105
80 106
81 107
82 108 if __name__ == "__main__":
83 109 B_MANAGER = BackupManager('/home/pylons/id_rsa', 'repositories.config')
84 110 B_MANAGER.backup_repos()
85 111 B_MANAGER.transfer_files()
86 112 B_MANAGER.rm_file()
87 113
88 114
@@ -1,97 +1,123 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # database managment for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 """
22 Created on April 10, 2010
23 database managment and creation for hg app
24 @author: marcink
25 """
26
1 27 from os.path import dirname as dn, join as jn
2 28 import os
3 29 import sys
4 30 ROOT = dn(dn(dn(os.path.realpath(__file__))))
5 31 sys.path.append(ROOT)
6 32
7 33 from pylons_app.lib.auth import get_crypt_password
8 34 from pylons_app.model import init_model
9 35 from pylons_app.model.db import User, Permission
10 36 from pylons_app.model.meta import Session, Base
11 37 from sqlalchemy.engine import create_engine
12 38 import logging
13 39
14 40 log = logging.getLogger('db manage')
15 41 log.setLevel(logging.DEBUG)
16 42 console_handler = logging.StreamHandler()
17 43 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
18 44 " %(levelname)-5.5s [%(name)s] %(message)s"))
19 45 log.addHandler(console_handler)
20 46
21 47 class DbManage(object):
22 48 def __init__(self, log_sql):
23 49 self.dbname = 'hg_app.db'
24 50 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
25 51 engine = create_engine(dburi, echo=log_sql)
26 52 init_model(engine)
27 53 self.sa = Session()
28 54 self.db_exists = False
29 55
30 56 def check_for_db(self, override):
31 57 log.info('checking for exisiting db')
32 58 if os.path.isfile(jn(ROOT, self.dbname)):
33 59 self.db_exists = True
34 60 log.info('database exisist')
35 61 if not override:
36 62 raise Exception('database already exists')
37 63
38 64 def create_tables(self, override=False):
39 65 """
40 66 Create a auth database
41 67 """
42 68 self.check_for_db(override)
43 69 if override:
44 70 log.info("database exisist and it's going to be destroyed")
45 71 if self.db_exists:
46 72 os.remove(jn(ROOT, self.dbname))
47 73 Base.metadata.create_all(checkfirst=override)
48 74 log.info('Created tables for %s', self.dbname)
49 75
50 76 def admin_prompt(self):
51 77 import getpass
52 78 username = raw_input('Specify admin username:')
53 79 password = getpass.getpass('Specify admin password:')
54 80 self.create_user(username, password, True)
55 81
56 82 def create_user(self, username, password, admin=False):
57 83 log.info('creating administrator user %s', username)
58 84
59 85 new_user = User()
60 86 new_user.username = username
61 87 new_user.password = get_crypt_password(password)
62 88 new_user.admin = admin
63 89 new_user.active = True
64 90
65 91 try:
66 92 self.sa.add(new_user)
67 93 self.sa.commit()
68 94 except:
69 95 self.sa.rollback()
70 96 raise
71 97
72 98 def create_permissions(self):
73 99 #module.(access|create|change|delete)_[name]
74 100 perms = [('admin.access_home', 'Access to admin user view'),
75 101
76 102 ]
77 103
78 104 for p in perms:
79 105 new_perm = Permission()
80 106 new_perm.permission_name = p[0]
81 107 new_perm.permission_longname = p[1]
82 108 try:
83 109 self.sa.add(new_perm)
84 110 self.sa.commit()
85 111 except:
86 112 self.sa.rollback()
87 113 raise
88 114
89 115
90 116
91 117 if __name__ == '__main__':
92 118 dbmanage = DbManage(log_sql=True)
93 119 dbmanage.create_tables(override=True)
94 120 dbmanage.admin_prompt()
95 121 dbmanage.create_permissions()
96 122
97 123
@@ -1,23 +1,49 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # simple filters for hg apps html templates
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 """
22 Created on April 12, 2010
23 simple filters for hg apps html templates
24 @author: marcink
25 """
26
1 27 from mercurial import util
2 28 from mercurial.templatefilters import age as _age, person as _person
3 29 from string import punctuation
4 30
5 31 def clean_repo(repo_name):
6 32 for x in punctuation:
7 33 if x != '_':
8 34 repo_name = repo_name.replace(x, '')
9 35 repo_name = repo_name.lower().strip()
10 36 return repo_name.replace(' ', '_')
11 37
12 38 age = lambda x:_age(x)
13 39 capitalize = lambda x: x.capitalize()
14 40 date = lambda x: util.datestr(x)
15 41 email = util.email
16 42 person = lambda x: _person(x)
17 43 hgdate = lambda x: "%d %d" % x
18 44 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
19 45 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
20 46 localdate = lambda x: (x[0], util.makedate()[1])
21 47 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
22 48 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
23 49 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
@@ -1,21 +1,47 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # middleware to handle https correctly
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 """
22 Created on May 23, 2010
23
24 @author: marcink
25 """
26
1 27 class HttpsFixup(object):
2 28 def __init__(self, app):
3 29 self.application = app
4 30
5 31 def __call__(self, environ, start_response):
6 32 self.__fixup(environ)
7 33 return self.application(environ, start_response)
8 34
9 35
10 36 def __fixup(self, environ):
11 37 """Function to fixup the environ as needed. In order to use this
12 38 middleware you should set this header inside your
13 39 proxy ie. nginx, apache etc.
14 40 """
15 41 proto = environ.get('HTTP_X_URL_SCHEME')
16 42
17 43 if proto == 'https':
18 44 environ['wsgi.url_scheme'] = proto
19 45 else:
20 46 environ['wsgi.url_scheme'] = 'http'
21 47 return None
@@ -1,138 +1,153 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
3 10 #
4 # Copyright (c) 2010 marcink. All rights reserved.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
5 15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
6 21 """
7 22 Created on 2010-04-28
8 23
9 24 @author: marcink
10 25 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
11 26 It's implemented with basic auth function
12 27 """
13 28 from datetime import datetime
14 29 from mercurial.hgweb import hgweb
15 30 from mercurial.hgweb.request import wsgiapplication
16 31 from paste.auth.basic import AuthBasicAuthenticator
17 32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
18 33 from pylons_app.lib.auth import authfunc
19 34 from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache
20 35 from pylons_app.model import meta
21 36 from pylons_app.model.db import UserLog, User
22 37 from webob.exc import HTTPNotFound
23 38 import logging
24 39 import os
25 40 log = logging.getLogger(__name__)
26 41
27 42 class SimpleHg(object):
28 43
29 44 def __init__(self, application, config):
30 45 self.application = application
31 46 self.config = config
32 47 #authenticate this mercurial request using
33 48 realm = '%s %s' % (config['hg_app_name'], 'mercurial repository')
34 49 self.authenticate = AuthBasicAuthenticator(realm, authfunc)
35 50
36 51 def __call__(self, environ, start_response):
37 52 if not is_mercurial(environ):
38 53 return self.application(environ, start_response)
39 54 else:
40 55 #===================================================================
41 56 # AUTHENTICATE THIS MERCURIAL REQUEST
42 57 #===================================================================
43 58 username = REMOTE_USER(environ)
44 59 if not username:
45 60 result = self.authenticate(environ)
46 61 if isinstance(result, str):
47 62 AUTH_TYPE.update(environ, 'basic')
48 63 REMOTE_USER.update(environ, result)
49 64 else:
50 65 return result.wsgi_application(environ, start_response)
51 66
52 67 try:
53 68 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
54 69 except Exception as e:
55 70 log.error(e)
56 71 return HTTPNotFound()(environ, start_response)
57 72
58 73 #since we wrap into hgweb, just reset the path
59 74 environ['PATH_INFO'] = '/'
60 75 self.baseui = make_ui(self.config['hg_app_repo_conf'])
61 76 self.basepath = self.baseui.configitems('paths')[0][1]\
62 77 .replace('*', '')
63 78 self.repo_path = os.path.join(self.basepath, repo_name)
64 79 try:
65 80 app = wsgiapplication(self.__make_app)
66 81 except Exception as e:
67 82 log.error(e)
68 83 return HTTPNotFound()(environ, start_response)
69 84 action = self.__get_action(environ)
70 85 #invalidate cache on push
71 86 if action == 'push':
72 87 self.__invalidate_cache(repo_name)
73 88
74 89 if action:
75 90 username = self.__get_environ_user(environ)
76 91 self.__log_user_action(username, action, repo_name)
77 92
78 93 return app(environ, start_response)
79 94
80 95 def __make_app(self):
81 96 hgserve = hgweb(self.repo_path)
82 97 return self.__load_web_settings(hgserve)
83 98
84 99 def __get_environ_user(self, environ):
85 100 return environ.get('REMOTE_USER')
86 101
87 102 def __get_action(self, environ):
88 103 """
89 104 Maps mercurial request commands into a pull or push command.
90 105 @param environ:
91 106 """
92 107 mapping = {
93 108 'changegroup': 'pull',
94 109 'changegroupsubset': 'pull',
95 110 'unbundle': 'push',
96 111 'stream_out': 'pull',
97 112 }
98 113 for qry in environ['QUERY_STRING'].split('&'):
99 114 if qry.startswith('cmd'):
100 115 cmd = qry.split('=')[-1]
101 116 if mapping.has_key(cmd):
102 117 return mapping[cmd]
103 118
104 119 def __log_user_action(self, username, action, repo):
105 120 sa = meta.Session
106 121 try:
107 122 user = sa.query(User).filter(User.username == username).one()
108 123 user_log = UserLog()
109 124 user_log.user_id = user.user_id
110 125 user_log.action = action
111 126 user_log.repository = repo.replace('/', '')
112 127 user_log.action_date = datetime.now()
113 128 sa.add(user_log)
114 129 sa.commit()
115 130 log.info('Adding user %s, action %s on %s',
116 131 username, action, repo)
117 132 except Exception as e:
118 133 sa.rollback()
119 134 log.error('could not log user action:%s', str(e))
120 135
121 136 def __invalidate_cache(self, repo_name):
122 137 """we know that some change was made to repositories and we should
123 138 invalidate the cache to see the changes right away but only for
124 139 push requests"""
125 140 invalidate_cache('cached_repo_list')
126 141 invalidate_cache('full_changelog', repo_name)
127 142
128 143
129 144 def __load_web_settings(self, hgserve):
130 145 repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False)
131 146 #set the global ui for hgserve
132 147 hgserve.repo.ui = self.baseui
133 148
134 149 if repoui:
135 150 #set the repository based config
136 151 hgserve.repo.ui = repoui
137 152
138 153 return hgserve
@@ -1,134 +1,160 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 """
22 Created on April 18, 2010
23 Utilities for hg app
24 @author: marcink
25 """
26
1 27 import os
2 28 import logging
3 29 from mercurial import ui, config, hg
4 30 from mercurial.error import RepoError
5 31 log = logging.getLogger(__name__)
6 32
7 33
8 34 def get_repo_slug(request):
9 35 return request.environ['pylons.routes_dict'].get('repo_name')
10 36
11 37 def is_mercurial(environ):
12 38 """
13 39 Returns True if request's target is mercurial server - header
14 40 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
15 41 """
16 42 http_accept = environ.get('HTTP_ACCEPT')
17 43 if http_accept and http_accept.startswith('application/mercurial'):
18 44 return True
19 45 return False
20 46
21 47 def check_repo_dir(paths):
22 48 repos_path = paths[0][1].split('/')
23 49 if repos_path[-1] in ['*', '**']:
24 50 repos_path = repos_path[:-1]
25 51 if repos_path[0] != '/':
26 52 repos_path[0] = '/'
27 53 if not os.path.isdir(os.path.join(*repos_path)):
28 54 raise Exception('Not a valid repository in %s' % paths[0][1])
29 55
30 56 def check_repo(repo_name, base_path):
31 57
32 58 repo_path = os.path.join(base_path, repo_name)
33 59
34 60 try:
35 61 r = hg.repository(ui.ui(), repo_path)
36 62 hg.verify(r)
37 63 #here we hnow that repo exists it was verified
38 64 log.info('%s repo is already created', repo_name)
39 65 return False
40 66 #raise Exception('Repo exists')
41 67 except RepoError:
42 68 log.info('%s repo is free for creation', repo_name)
43 69 #it means that there is no valid repo there...
44 70 return True
45 71
46 72 def make_ui(path=None, checkpaths=True):
47 73 """
48 74 A funcion that will read python rc files and make an ui from read options
49 75
50 76 @param path: path to mercurial config file
51 77 """
52 78 if not path:
53 79 log.error('repos config path is empty !')
54 80
55 81 if not os.path.isfile(path):
56 82 log.warning('Unable to read config file %s' % path)
57 83 return False
58 84 #propagated from mercurial documentation
59 85 sections = [
60 86 'alias',
61 87 'auth',
62 88 'decode/encode',
63 89 'defaults',
64 90 'diff',
65 91 'email',
66 92 'extensions',
67 93 'format',
68 94 'merge-patterns',
69 95 'merge-tools',
70 96 'hooks',
71 97 'http_proxy',
72 98 'smtp',
73 99 'patch',
74 100 'paths',
75 101 'profiling',
76 102 'server',
77 103 'trusted',
78 104 'ui',
79 105 'web',
80 106 ]
81 107
82 108 baseui = ui.ui()
83 109 cfg = config.config()
84 110 cfg.read(path)
85 111 if checkpaths:check_repo_dir(cfg.items('paths'))
86 112
87 113 for section in sections:
88 114 for k, v in cfg.items(section):
89 115 baseui.setconfig(section, k, v)
90 116
91 117 return baseui
92 118
93 119 def invalidate_cache(name, *args):
94 120 """Invalidates given name cache"""
95 121
96 122 from beaker.cache import region_invalidate
97 123 log.info('INVALIDATING CACHE FOR %s', name)
98 124
99 125 """propagate our arguments to make sure invalidation works. First
100 126 argument has to be the name of cached func name give to cache decorator
101 127 without that the invalidation would not work"""
102 128 tmp = [name]
103 129 tmp.extend(args)
104 130 args = tuple(tmp)
105 131
106 132 if name == 'cached_repo_list':
107 133 from pylons_app.model.hg_model import _get_repos_cached
108 134 region_invalidate(_get_repos_cached, None, *args)
109 135
110 136 if name == 'full_changelog':
111 137 from pylons_app.model.hg_model import _full_changelog_cached
112 138 region_invalidate(_full_changelog_cached, None, *args)
113 139
114 140 from vcs.backends.base import BaseChangeset
115 141 from vcs.utils.lazy import LazyProperty
116 142 class EmptyChangeset(BaseChangeset):
117 143
118 144 revision = -1
119 145
120 146 @LazyProperty
121 147 def raw_id(self):
122 148 """
123 149 Returns raw string identifing this changeset, useful for web
124 150 representation.
125 151 """
126 152 return '0' * 12
127 153
128 154
129 155 def repo2db_mapper():
130 156 """
131 157 scann all dirs for .hgdbid
132 158 if some dir doesn't have one generate one.
133 159 """
134 pass No newline at end of file
160 pass
@@ -1,126 +1,141 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 #
4 # Copyright (c) 2010 marcink. All rights reserved.
3 # Model for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
5 10 #
6 '''
7 Created on Apr 9, 2010
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
8 20
21 """
22 Created on April 9, 2010
23 Model for hg app
9 24 @author: marcink
10 '''
25 """
11 26
12 27 from beaker.cache import cache_region
13 28 from mercurial import ui
14 29 from mercurial.hgweb.hgwebdir_mod import findrepos
15 30 from pylons import app_globals as g
16 31 from vcs.exceptions import RepositoryError, VCSError
17 32 import logging
18 33 import os
19 34 import sys
20 35 log = logging.getLogger(__name__)
21 36
22 37 try:
23 38 from vcs.backends.hg import MercurialRepository
24 39 except ImportError:
25 40 sys.stderr.write('You have to import vcs module')
26 41 raise Exception('Unable to import vcs')
27 42
28 43
29 44 @cache_region('long_term', 'cached_repo_list')
30 45 def _get_repos_cached():
31 46 """
32 47 return cached dict with repos
33 48 """
34 49 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
35 50
36 51 @cache_region('long_term', 'full_changelog')
37 52 def _full_changelog_cached(repo_name):
38 53 log.info('getting full changelog for %s', repo_name)
39 54 return list(reversed(list(HgModel().get_repo(repo_name))))
40 55
41 56 class HgModel(object):
42 57 """
43 58 Mercurial Model
44 59 """
45 60
46 61 def __init__(self):
47 62 """
48 63 Constructor
49 64 """
50 65 pass
51 66
52 67 @staticmethod
53 68 def repo_scan(repos_prefix, repos_path, baseui):
54 69 """
55 70 Listing of repositories in given path. This path should not be a
56 71 repository itself. Return a dictionary of repository objects
57 72 :param repos_path: path to directory it could take syntax with
58 73 * or ** for deep recursive displaying repositories
59 74 """
60 75 def check_repo_dir(path):
61 76 """
62 77 Checks the repository
63 78 :param path:
64 79 """
65 80 repos_path = path.split('/')
66 81 if repos_path[-1] in ['*', '**']:
67 82 repos_path = repos_path[:-1]
68 83 if repos_path[0] != '/':
69 84 repos_path[0] = '/'
70 85 if not os.path.isdir(os.path.join(*repos_path)):
71 86 raise RepositoryError('Not a valid repository in %s' % path[0][1])
72 87 if not repos_path.endswith('*'):
73 88 raise VCSError('You need to specify * or ** at the end of path '
74 89 'for recursive scanning')
75 90
76 91 check_repo_dir(repos_path)
77 92 log.info('scanning for repositories in %s', repos_path)
78 93 repos = findrepos([(repos_prefix, repos_path)])
79 94 if not isinstance(baseui, ui.ui):
80 95 baseui = ui.ui()
81 96
82 97 repos_list = {}
83 98 for name, path in repos:
84 99 try:
85 100 #name = name.split('/')[-1]
86 101 if repos_list.has_key(name):
87 102 raise RepositoryError('Duplicate repository name %s found in'
88 103 ' %s' % (name, path))
89 104 else:
90 105 repos_list[name] = MercurialRepository(path, baseui=baseui)
91 106 repos_list[name].name = name
92 107 except OSError:
93 108 continue
94 109 return repos_list
95 110
96 111 def get_repos(self):
97 112 for name, repo in _get_repos_cached().items():
98 113 if repo._get_hidden():
99 114 #skip hidden web repository
100 115 continue
101 116
102 117 last_change = repo.last_change
103 118 try:
104 119 tip = repo.get_changeset('tip')
105 120 except RepositoryError:
106 121 from pylons_app.lib.utils import EmptyChangeset
107 122 tip = EmptyChangeset()
108 123
109 124 tmp_d = {}
110 125 tmp_d['name'] = repo.name
111 126 tmp_d['name_sort'] = tmp_d['name'].lower()
112 127 tmp_d['description'] = repo.description
113 128 tmp_d['description_sort'] = tmp_d['description']
114 129 tmp_d['last_change'] = last_change
115 130 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
116 131 tmp_d['tip'] = tip.raw_id
117 132 tmp_d['tip_sort'] = tip.revision
118 133 tmp_d['rev'] = tip.revision
119 134 tmp_d['contact'] = repo.contact
120 135 tmp_d['contact_sort'] = tmp_d['contact']
121 136 tmp_d['repo_archives'] = list(repo._get_archives())
122 137
123 138 yield tmp_d
124 139
125 140 def get_repo(self, repo_name):
126 141 return _get_repos_cached()[repo_name]
@@ -1,48 +1,64 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 # Model for users
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
3 10 #
4 # Copyright (c) 2010 marcink. All rights reserved.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
5 15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 """
22 Created on April 9, 2010
23 Model for users
24 @author: marcink
25 """
26
6 27 from pylons_app.model.db import User
7 28 from pylons_app.model.meta import Session
8 '''
9 Created on Apr 9, 2010
10
11 @author: marcink
12 '''
13 29
14 30 class UserModel(object):
15 31
16 32 def __init__(self):
17 33 self.sa = Session()
18 34
19 35 def get_user(self, id):
20 36 return self.sa.query(User).get(id)
21 37
22 38 def create(self, form_data):
23 39 try:
24 40 new_user = User()
25 41 for k, v in form_data.items():
26 42 setattr(new_user, k, v)
27 43
28 44 self.sa.add(new_user)
29 45 self.sa.commit()
30 46 except:
31 47 self.sa.rollback()
32 48 raise
33 49
34 50 def update(self, id, form_data):
35 51 try:
36 52 new_user = self.sa.query(User).get(id)
37 53 for k, v in form_data.items():
38 54 if k == 'new_password' and v != '':
39 55
40 56 new_user.password = v
41 57 else:
42 58 setattr(new_user, k, v)
43 59
44 60 self.sa.add(new_user)
45 61 self.sa.commit()
46 62 except:
47 63 self.sa.rollback()
48 64 raise
@@ -1,21 +1,21 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Permissions administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 10 ${_('Permissions')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('permissions')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('Permissions')}</h2>
19 todo :)
19
20 20 </div>
21 21 </%def>
General Comments 0
You need to be logged in to leave comments. Login now