##// END OF EJS Templates
refactoring for new vcs implementation...
marcink -
r512:d945c95b default
parent child Browse files
Show More
@@ -1,74 +1,74
1 # List of modules to import when celery starts.
1 # List of modules to import when celery starts.
2 import sys
2 import sys
3 import os
3 import os
4 import ConfigParser
4 import ConfigParser
5 root = os.getcwd()
5 root = os.getcwd()
6
6
7 PYLONS_CONFIG_NAME = 'development.ini'
7 PYLONS_CONFIG_NAME = 'production.ini'
8
8
9 sys.path.append(root)
9 sys.path.append(root)
10 config = ConfigParser.ConfigParser({'here':root})
10 config = ConfigParser.ConfigParser({'here':root})
11 config.read('%s/%s' % (root, PYLONS_CONFIG_NAME))
11 config.read('%s/%s' % (root, PYLONS_CONFIG_NAME))
12 PYLONS_CONFIG = config
12 PYLONS_CONFIG = config
13
13
14 CELERY_IMPORTS = ("pylons_app.lib.celerylib.tasks",)
14 CELERY_IMPORTS = ("pylons_app.lib.celerylib.tasks",)
15
15
16 ## Result store settings.
16 ## Result store settings.
17 CELERY_RESULT_BACKEND = "database"
17 CELERY_RESULT_BACKEND = "database"
18 CELERY_RESULT_DBURI = dict(config.items('app:main'))['sqlalchemy.db1.url']
18 CELERY_RESULT_DBURI = dict(config.items('app:main'))['sqlalchemy.db1.url']
19 CELERY_RESULT_SERIALIZER = 'json'
19 CELERY_RESULT_SERIALIZER = 'json'
20
20
21
21
22 BROKER_CONNECTION_MAX_RETRIES = 30
22 BROKER_CONNECTION_MAX_RETRIES = 30
23
23
24 ## Broker settings.
24 ## Broker settings.
25 BROKER_HOST = "localhost"
25 BROKER_HOST = "localhost"
26 BROKER_PORT = 5672
26 BROKER_PORT = 5672
27 BROKER_VHOST = "rabbitmqhost"
27 BROKER_VHOST = "rabbitmqhost"
28 BROKER_USER = "rabbitmq"
28 BROKER_USER = "rabbitmq"
29 BROKER_PASSWORD = "qweqwe"
29 BROKER_PASSWORD = "qweqwe"
30
30
31 ## Worker settings
31 ## Worker settings
32 ## If you're doing mostly I/O you can have more processes,
32 ## If you're doing mostly I/O you can have more processes,
33 ## but if mostly spending CPU, try to keep it close to the
33 ## but if mostly spending CPU, try to keep it close to the
34 ## number of CPUs on your machine. If not set, the number of CPUs/cores
34 ## number of CPUs on your machine. If not set, the number of CPUs/cores
35 ## available will be used.
35 ## available will be used.
36 CELERYD_CONCURRENCY = 2
36 CELERYD_CONCURRENCY = 2
37 # CELERYD_LOG_FILE = "celeryd.log"
37 # CELERYD_LOG_FILE = "celeryd.log"
38 CELERYD_LOG_LEVEL = "DEBUG"
38 CELERYD_LOG_LEVEL = "DEBUG"
39 CELERYD_MAX_TASKS_PER_CHILD = 3
39 CELERYD_MAX_TASKS_PER_CHILD = 3
40
40
41 #Tasks will never be sent to the queue, but executed locally instead.
41 #Tasks will never be sent to the queue, but executed locally instead.
42 CELERY_ALWAYS_EAGER = False
42 CELERY_ALWAYS_EAGER = False
43
43
44 #===============================================================================
44 #===============================================================================
45 # EMAIL SETTINGS
45 # EMAIL SETTINGS
46 #===============================================================================
46 #===============================================================================
47 pylons_email_config = dict(config.items('DEFAULT'))
47 pylons_email_config = dict(config.items('DEFAULT'))
48
48
49 CELERY_SEND_TASK_ERROR_EMAILS = True
49 CELERY_SEND_TASK_ERROR_EMAILS = True
50
50
51 #List of (name, email_address) tuples for the admins that should receive error e-mails.
51 #List of (name, email_address) tuples for the admins that should receive error e-mails.
52 ADMINS = [('Administrator', pylons_email_config.get('email_to'))]
52 ADMINS = [('Administrator', pylons_email_config.get('email_to'))]
53
53
54 #The e-mail address this worker sends e-mails from. Default is "celery@localhost".
54 #The e-mail address this worker sends e-mails from. Default is "celery@localhost".
55 SERVER_EMAIL = pylons_email_config.get('error_email_from')
55 SERVER_EMAIL = pylons_email_config.get('error_email_from')
56
56
57 #The mail server to use. Default is "localhost".
57 #The mail server to use. Default is "localhost".
58 MAIL_HOST = pylons_email_config.get('smtp_server')
58 MAIL_HOST = pylons_email_config.get('smtp_server')
59
59
60 #Username (if required) to log on to the mail server with.
60 #Username (if required) to log on to the mail server with.
61 MAIL_HOST_USER = pylons_email_config.get('smtp_username')
61 MAIL_HOST_USER = pylons_email_config.get('smtp_username')
62
62
63 #Password (if required) to log on to the mail server with.
63 #Password (if required) to log on to the mail server with.
64 MAIL_HOST_PASSWORD = pylons_email_config.get('smtp_password')
64 MAIL_HOST_PASSWORD = pylons_email_config.get('smtp_password')
65
65
66 MAIL_PORT = pylons_email_config.get('smtp_port')
66 MAIL_PORT = pylons_email_config.get('smtp_port')
67
67
68
68
69 #===============================================================================
69 #===============================================================================
70 # INSTRUCTIONS FOR RABBITMQ
70 # INSTRUCTIONS FOR RABBITMQ
71 #===============================================================================
71 #===============================================================================
72 # rabbitmqctl add_user rabbitmq qweqwe
72 # rabbitmqctl add_user rabbitmq qweqwe
73 # rabbitmqctl add_vhost rabbitmqhost
73 # rabbitmqctl add_vhost rabbitmqhost
74 # rabbitmqctl set_permissions -p rabbitmqhost rabbitmq ".*" ".*" ".*"
74 # rabbitmqctl set_permissions -p rabbitmqhost rabbitmq ".*" ".*" ".*"
@@ -1,138 +1,138
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # changeset controller for pylons
3 # changeset controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 """
19 """
20 Created on April 25, 2010
20 Created on April 25, 2010
21 changeset controller for pylons
21 changeset controller for pylons
22 @author: marcink
22 @author: marcink
23 """
23 """
24 from pylons import tmpl_context as c, url, request, response
24 from pylons import tmpl_context as c, url, request, response
25 from pylons.controllers.util import redirect
25 from pylons.controllers.util import redirect
26 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
26 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from pylons_app.lib.base import BaseController, render
27 from pylons_app.lib.base import BaseController, render
28 from pylons_app.model.hg_model import HgModel
28 from pylons_app.model.hg_model import HgModel
29 from vcs.exceptions import RepositoryError
29 from vcs.exceptions import RepositoryError
30 from vcs.nodes import FileNode
30 from vcs.nodes import FileNode
31 from vcs.utils import diffs as differ
31 from vcs.utils import diffs as differ
32 import logging
32 import logging
33 import traceback
33 import traceback
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 class ChangesetController(BaseController):
37 class ChangesetController(BaseController):
38
38
39 @LoginRequired()
39 @LoginRequired()
40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 'repository.admin')
41 'repository.admin')
42 def __before__(self):
42 def __before__(self):
43 super(ChangesetController, self).__before__()
43 super(ChangesetController, self).__before__()
44
44
45 def index(self, revision):
45 def index(self, revision):
46 hg_model = HgModel()
46 hg_model = HgModel()
47 try:
47 try:
48 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
48 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
49 except RepositoryError:
49 except RepositoryError:
50 log.error(traceback.format_exc())
50 log.error(traceback.format_exc())
51 return redirect(url('hg_home'))
51 return redirect(url('hg_home'))
52 else:
52 else:
53 try:
53 try:
54 c.changeset_old = c.changeset.parents[0]
54 c.changeset_old = c.changeset.parents[0]
55 except IndexError:
55 except IndexError:
56 c.changeset_old = None
56 c.changeset_old = None
57 c.changes = []
57 c.changes = []
58
58
59 for node in c.changeset.added:
59 for node in c.changeset.added:
60 filenode_old = FileNode(node.path, '')
60 filenode_old = FileNode(node.path, '')
61 if filenode_old.is_binary or node.is_binary:
61 if filenode_old.is_binary or node.is_binary:
62 diff = 'binary file'
62 diff = 'binary file'
63 else:
63 else:
64 f_udiff = differ.get_udiff(filenode_old, node)
64 f_udiff = differ.get_udiff(filenode_old, node)
65 diff = differ.DiffProcessor(f_udiff).as_html()
65 diff = differ.DiffProcessor(f_udiff).as_html()
66
66
67 cs1 = None
67 cs1 = None
68 cs2 = node.last_changeset.raw_id
68 cs2 = node.last_changeset.short_id
69 c.changes.append(('added', node, diff, cs1, cs2))
69 c.changes.append(('added', node, diff, cs1, cs2))
70
70
71 for node in c.changeset.changed:
71 for node in c.changeset.changed:
72 filenode_old = c.changeset_old.get_node(node.path)
72 filenode_old = c.changeset_old.get_node(node.path)
73 if filenode_old.is_binary or node.is_binary:
73 if filenode_old.is_binary or node.is_binary:
74 diff = 'binary file'
74 diff = 'binary file'
75 else:
75 else:
76 f_udiff = differ.get_udiff(filenode_old, node)
76 f_udiff = differ.get_udiff(filenode_old, node)
77 diff = differ.DiffProcessor(f_udiff).as_html()
77 diff = differ.DiffProcessor(f_udiff).as_html()
78
78
79 cs1 = filenode_old.last_changeset.raw_id
79 cs1 = filenode_old.last_changeset.short_id
80 cs2 = node.last_changeset.raw_id
80 cs2 = node.last_changeset.short_id
81 c.changes.append(('changed', node, diff, cs1, cs2))
81 c.changes.append(('changed', node, diff, cs1, cs2))
82
82
83 for node in c.changeset.removed:
83 for node in c.changeset.removed:
84 c.changes.append(('removed', node, None, None, None))
84 c.changes.append(('removed', node, None, None, None))
85
85
86 return render('changeset/changeset.html')
86 return render('changeset/changeset.html')
87
87
88 def raw_changeset(self,revision):
88 def raw_changeset(self,revision):
89
89
90 hg_model = HgModel()
90 hg_model = HgModel()
91 method = request.GET.get('diff','show')
91 method = request.GET.get('diff','show')
92 try:
92 try:
93 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
93 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
94 except RepositoryError:
94 except RepositoryError:
95 log.error(traceback.format_exc())
95 log.error(traceback.format_exc())
96 return redirect(url('hg_home'))
96 return redirect(url('hg_home'))
97 else:
97 else:
98 try:
98 try:
99 c.changeset_old = c.changeset.parents[0]
99 c.changeset_old = c.changeset.parents[0]
100 except IndexError:
100 except IndexError:
101 c.changeset_old = None
101 c.changeset_old = None
102 c.changes = []
102 c.changes = []
103
103
104 for node in c.changeset.added:
104 for node in c.changeset.added:
105 filenode_old = FileNode(node.path, '')
105 filenode_old = FileNode(node.path, '')
106 if filenode_old.is_binary or node.is_binary:
106 if filenode_old.is_binary or node.is_binary:
107 diff = 'binary file'
107 diff = 'binary file'
108 else:
108 else:
109 f_udiff = differ.get_udiff(filenode_old, node)
109 f_udiff = differ.get_udiff(filenode_old, node)
110 diff = differ.DiffProcessor(f_udiff).raw_diff()
110 diff = differ.DiffProcessor(f_udiff).raw_diff()
111
111
112 cs1 = None
112 cs1 = None
113 cs2 = node.last_changeset.raw_id
113 cs2 = node.last_changeset.short_id
114 c.changes.append(('added', node, diff, cs1, cs2))
114 c.changes.append(('added', node, diff, cs1, cs2))
115
115
116 for node in c.changeset.changed:
116 for node in c.changeset.changed:
117 filenode_old = c.changeset_old.get_node(node.path)
117 filenode_old = c.changeset_old.get_node(node.path)
118 if filenode_old.is_binary or node.is_binary:
118 if filenode_old.is_binary or node.is_binary:
119 diff = 'binary file'
119 diff = 'binary file'
120 else:
120 else:
121 f_udiff = differ.get_udiff(filenode_old, node)
121 f_udiff = differ.get_udiff(filenode_old, node)
122 diff = differ.DiffProcessor(f_udiff).raw_diff()
122 diff = differ.DiffProcessor(f_udiff).raw_diff()
123
123
124 cs1 = filenode_old.last_changeset.raw_id
124 cs1 = filenode_old.last_changeset.short_id
125 cs2 = node.last_changeset.raw_id
125 cs2 = node.last_changeset.short_id
126 c.changes.append(('changed', node, diff, cs1, cs2))
126 c.changes.append(('changed', node, diff, cs1, cs2))
127
127
128 response.content_type = 'text/plain'
128 response.content_type = 'text/plain'
129 if method == 'download':
129 if method == 'download':
130 response.content_disposition = 'attachment; filename=%s.patch' % revision
130 response.content_disposition = 'attachment; filename=%s.patch' % revision
131 parent = True if len(c.changeset.parents) > 0 else False
131 parent = True if len(c.changeset.parents) > 0 else False
132 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0]._hex if parent else ''
132 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0]._hex if parent else ''
133
133
134 c.diffs = ''
134 c.diffs = ''
135 for x in c.changes:
135 for x in c.changes:
136 c.diffs += x[2]
136 c.diffs += x[2]
137
137
138 return render('changeset/raw_changeset.html')
138 return render('changeset/raw_changeset.html')
@@ -1,80 +1,80
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # feed controller for pylons
3 # feed controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 23, 2010
21 Created on April 23, 2010
22 feed controller for pylons
22 feed controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from pylons import tmpl_context as c, url, response
25 from pylons import tmpl_context as c, url, response
26 from pylons_app.lib.base import BaseController, render
26 from pylons_app.lib.base import BaseController, render
27 from pylons_app.model.hg_model import HgModel
27 from pylons_app.model.hg_model import HgModel
28 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
28 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
29 import logging
29 import logging
30 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
31
31
32 class FeedController(BaseController):
32 class FeedController(BaseController):
33
33
34 #secure it or not ?
34 #secure it or not ?
35 def __before__(self):
35 def __before__(self):
36 super(FeedController, self).__before__()
36 super(FeedController, self).__before__()
37 #common values for feeds
37 #common values for feeds
38 self.description = 'Changes on %s repository'
38 self.description = 'Changes on %s repository'
39 self.title = "%s feed"
39 self.title = "%s feed"
40 self.language = 'en-us'
40 self.language = 'en-us'
41 self.ttl = "5"
41 self.ttl = "5"
42 self.feed_nr = 10
42 self.feed_nr = 10
43
43
44 def atom(self, repo_name):
44 def atom(self, repo_name):
45 """Produce an atom-1.0 feed via feedgenerator module"""
45 """Produce an atom-1.0 feed via feedgenerator module"""
46 feed = Atom1Feed(title=self.title % repo_name,
46 feed = Atom1Feed(title=self.title % repo_name,
47 link=url('summary_home', repo_name=repo_name, qualified=True),
47 link=url('summary_home', repo_name=repo_name, qualified=True),
48 description=self.description % repo_name,
48 description=self.description % repo_name,
49 language=self.language,
49 language=self.language,
50 ttl=self.ttl)
50 ttl=self.ttl)
51
51
52 changesets = HgModel().get_repo(repo_name)
52 changesets = HgModel().get_repo(repo_name)
53
53
54 for cs in changesets[:self.feed_nr]:
54 for cs in changesets[:self.feed_nr]:
55 feed.add_item(title=cs.message,
55 feed.add_item(title=cs.message,
56 link=url('changeset_home', repo_name=repo_name,
56 link=url('changeset_home', repo_name=repo_name,
57 revision=cs.raw_id, qualified=True),
57 revision=cs.short_id, qualified=True),
58 description=str(cs.date))
58 description=str(cs.date))
59
59
60 response.content_type = feed.mime_type
60 response.content_type = feed.mime_type
61 return feed.writeString('utf-8')
61 return feed.writeString('utf-8')
62
62
63
63
64 def rss(self, repo_name):
64 def rss(self, repo_name):
65 """Produce an rss2 feed via feedgenerator module"""
65 """Produce an rss2 feed via feedgenerator module"""
66 feed = Rss201rev2Feed(title=self.title % repo_name,
66 feed = Rss201rev2Feed(title=self.title % repo_name,
67 link=url('summary_home', repo_name=repo_name, qualified=True),
67 link=url('summary_home', repo_name=repo_name, qualified=True),
68 description=self.description % repo_name,
68 description=self.description % repo_name,
69 language=self.language,
69 language=self.language,
70 ttl=self.ttl)
70 ttl=self.ttl)
71
71
72 changesets = HgModel().get_repo(repo_name)
72 changesets = HgModel().get_repo(repo_name)
73 for cs in changesets[:self.feed_nr]:
73 for cs in changesets[:self.feed_nr]:
74 feed.add_item(title=cs.message,
74 feed.add_item(title=cs.message,
75 link=url('changeset_home', repo_name=repo_name,
75 link=url('changeset_home', repo_name=repo_name,
76 revision=cs.raw_id, qualified=True),
76 revision=cs.short_id, qualified=True),
77 description=str(cs.date))
77 description=str(cs.date))
78
78
79 response.content_type = feed.mime_type
79 response.content_type = feed.mime_type
80 return feed.writeString('utf-8')
80 return feed.writeString('utf-8')
@@ -1,207 +1,207
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # files controller for pylons
3 # files controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 21, 2010
21 Created on April 21, 2010
22 files controller for pylons
22 files controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from mercurial import archival
25 from mercurial import archival
26 from pylons import request, response, session, tmpl_context as c, url
26 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from pylons_app.lib.base import BaseController, render
29 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.utils import EmptyChangeset
30 from pylons_app.lib.utils import EmptyChangeset
31 from pylons_app.model.hg_model import HgModel
31 from pylons_app.model.hg_model import HgModel
32 from vcs.exceptions import RepositoryError, ChangesetError
32 from vcs.exceptions import RepositoryError, ChangesetError
33 from vcs.nodes import FileNode
33 from vcs.nodes import FileNode
34 from vcs.utils import diffs as differ
34 from vcs.utils import diffs as differ
35 import logging
35 import logging
36 import pylons_app.lib.helpers as h
36 import pylons_app.lib.helpers as h
37 import tempfile
37 import tempfile
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class FilesController(BaseController):
41 class FilesController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 'repository.admin')
45 'repository.admin')
46 def __before__(self):
46 def __before__(self):
47 super(FilesController, self).__before__()
47 super(FilesController, self).__before__()
48 c.file_size_limit = 250 * 1024 #limit of file size to display
48 c.file_size_limit = 250 * 1024 #limit of file size to display
49
49
50 def index(self, repo_name, revision, f_path):
50 def index(self, repo_name, revision, f_path):
51 hg_model = HgModel()
51 hg_model = HgModel()
52 c.repo = repo = hg_model.get_repo(c.repo_name)
52 c.repo = repo = hg_model.get_repo(c.repo_name)
53 revision = request.POST.get('at_rev', None) or revision
53 revision = request.POST.get('at_rev', None) or revision
54
54
55 def get_next_rev(cur):
55 def get_next_rev(cur):
56 max_rev = len(c.repo.revisions) - 1
56 max_rev = len(c.repo.revisions) - 1
57 r = cur + 1
57 r = cur + 1
58 if r > max_rev:
58 if r > max_rev:
59 r = max_rev
59 r = max_rev
60 return r
60 return r
61
61
62 def get_prev_rev(cur):
62 def get_prev_rev(cur):
63 r = cur - 1
63 r = cur - 1
64 return r
64 return r
65
65
66 c.f_path = f_path
66 c.f_path = f_path
67
67
68
68
69 try:
69 try:
70 cur_rev = repo.get_changeset(revision).revision
70 cur_rev = repo.get_changeset(revision).revision
71 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
71 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).short_id
72 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
72 next_rev = repo.get_changeset(get_next_rev(cur_rev)).short_id
73
73
74 c.url_prev = url('files_home', repo_name=c.repo_name,
74 c.url_prev = url('files_home', repo_name=c.repo_name,
75 revision=prev_rev, f_path=f_path)
75 revision=prev_rev, f_path=f_path)
76 c.url_next = url('files_home', repo_name=c.repo_name,
76 c.url_next = url('files_home', repo_name=c.repo_name,
77 revision=next_rev, f_path=f_path)
77 revision=next_rev, f_path=f_path)
78
78
79 c.changeset = repo.get_changeset(revision)
79 c.changeset = repo.get_changeset(revision)
80
80
81 c.cur_rev = c.changeset.raw_id
81 c.cur_rev = c.changeset.short_id
82 c.rev_nr = c.changeset.revision
82 c.rev_nr = c.changeset.revision
83 c.files_list = c.changeset.get_node(f_path)
83 c.files_list = c.changeset.get_node(f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
85
85
86 except (RepositoryError, ChangesetError):
86 except (RepositoryError, ChangesetError):
87 c.files_list = None
87 c.files_list = None
88
88
89 return render('files/files.html')
89 return render('files/files.html')
90
90
91 def rawfile(self, repo_name, revision, f_path):
91 def rawfile(self, repo_name, revision, f_path):
92 hg_model = HgModel()
92 hg_model = HgModel()
93 c.repo = hg_model.get_repo(c.repo_name)
93 c.repo = hg_model.get_repo(c.repo_name)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
95 response.content_type = file_node.mimetype
95 response.content_type = file_node.mimetype
96 response.content_disposition = 'attachment; filename=%s' \
96 response.content_disposition = 'attachment; filename=%s' \
97 % f_path.split('/')[-1]
97 % f_path.split('/')[-1]
98 return file_node.content
98 return file_node.content
99
99
100 def raw(self, repo_name, revision, f_path):
100 def raw(self, repo_name, revision, f_path):
101 hg_model = HgModel()
101 hg_model = HgModel()
102 c.repo = hg_model.get_repo(c.repo_name)
102 c.repo = hg_model.get_repo(c.repo_name)
103 file_node = c.repo.get_changeset(revision).get_node(f_path)
103 file_node = c.repo.get_changeset(revision).get_node(f_path)
104 response.content_type = 'text/plain'
104 response.content_type = 'text/plain'
105
105
106 return file_node.content
106 return file_node.content
107
107
108 def annotate(self, repo_name, revision, f_path):
108 def annotate(self, repo_name, revision, f_path):
109 hg_model = HgModel()
109 hg_model = HgModel()
110 c.repo = hg_model.get_repo(c.repo_name)
110 c.repo = hg_model.get_repo(c.repo_name)
111 cs = c.repo.get_changeset(revision)
111 cs = c.repo.get_changeset(revision)
112 c.file = cs.get_node(f_path)
112 c.file = cs.get_node(f_path)
113 c.file_msg = cs.get_file_message(f_path)
113 c.file_msg = cs.get_file_message(f_path)
114 c.cur_rev = cs.raw_id
114 c.cur_rev = cs.short_id
115 c.rev_nr = cs.revision
115 c.rev_nr = cs.revision
116 c.f_path = f_path
116 c.f_path = f_path
117
117
118 return render('files/files_annotate.html')
118 return render('files/files_annotate.html')
119
119
120 def archivefile(self, repo_name, revision, fileformat):
120 def archivefile(self, repo_name, revision, fileformat):
121 archive_specs = {
121 archive_specs = {
122 '.tar.bz2': ('application/x-tar', 'tbz2'),
122 '.tar.bz2': ('application/x-tar', 'tbz2'),
123 '.tar.gz': ('application/x-tar', 'tgz'),
123 '.tar.gz': ('application/x-tar', 'tgz'),
124 '.zip': ('application/zip', 'zip'),
124 '.zip': ('application/zip', 'zip'),
125 }
125 }
126 if not archive_specs.has_key(fileformat):
126 if not archive_specs.has_key(fileformat):
127 return 'Unknown archive type %s' % fileformat
127 return 'Unknown archive type %s' % fileformat
128
128
129 def read_in_chunks(file_object, chunk_size=1024 * 40):
129 def read_in_chunks(file_object, chunk_size=1024 * 40):
130 """Lazy function (generator) to read a file piece by piece.
130 """Lazy function (generator) to read a file piece by piece.
131 Default chunk size: 40k."""
131 Default chunk size: 40k."""
132 while True:
132 while True:
133 data = file_object.read(chunk_size)
133 data = file_object.read(chunk_size)
134 if not data:
134 if not data:
135 break
135 break
136 yield data
136 yield data
137
137
138 archive = tempfile.TemporaryFile()
138 archive = tempfile.TemporaryFile()
139 repo = HgModel().get_repo(repo_name).repo
139 repo = HgModel().get_repo(repo_name).repo
140 fname = '%s-%s%s' % (repo_name, revision, fileformat)
140 fname = '%s-%s%s' % (repo_name, revision, fileformat)
141 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
141 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
142 prefix='%s-%s' % (repo_name, revision))
142 prefix='%s-%s' % (repo_name, revision))
143 response.content_type = archive_specs[fileformat][0]
143 response.content_type = archive_specs[fileformat][0]
144 response.content_disposition = 'attachment; filename=%s' % fname
144 response.content_disposition = 'attachment; filename=%s' % fname
145 archive.seek(0)
145 archive.seek(0)
146 return read_in_chunks(archive)
146 return read_in_chunks(archive)
147
147
148 def diff(self, repo_name, f_path):
148 def diff(self, repo_name, f_path):
149 hg_model = HgModel()
149 hg_model = HgModel()
150 diff1 = request.GET.get('diff1')
150 diff1 = request.GET.get('diff1')
151 diff2 = request.GET.get('diff2')
151 diff2 = request.GET.get('diff2')
152 c.action = request.GET.get('diff')
152 c.action = request.GET.get('diff')
153 c.no_changes = diff1 == diff2
153 c.no_changes = diff1 == diff2
154 c.f_path = f_path
154 c.f_path = f_path
155 c.repo = hg_model.get_repo(c.repo_name)
155 c.repo = hg_model.get_repo(c.repo_name)
156
156
157 try:
157 try:
158 if diff1 not in ['', None, 'None', '0' * 12]:
158 if diff1 not in ['', None, 'None', '0' * 12]:
159 c.changeset_1 = c.repo.get_changeset(diff1)
159 c.changeset_1 = c.repo.get_changeset(diff1)
160 node1 = c.changeset_1.get_node(f_path)
160 node1 = c.changeset_1.get_node(f_path)
161 else:
161 else:
162 c.changeset_1 = EmptyChangeset()
162 c.changeset_1 = EmptyChangeset()
163 node1 = FileNode('.', '')
163 node1 = FileNode('.', '')
164 if diff2 not in ['', None, 'None', '0' * 12]:
164 if diff2 not in ['', None, 'None', '0' * 12]:
165 c.changeset_2 = c.repo.get_changeset(diff2)
165 c.changeset_2 = c.repo.get_changeset(diff2)
166 node2 = c.changeset_2.get_node(f_path)
166 node2 = c.changeset_2.get_node(f_path)
167 else:
167 else:
168 c.changeset_2 = EmptyChangeset()
168 c.changeset_2 = EmptyChangeset()
169 node2 = FileNode('.', '')
169 node2 = FileNode('.', '')
170 except RepositoryError:
170 except RepositoryError:
171 return redirect(url('files_home',
171 return redirect(url('files_home',
172 repo_name=c.repo_name, f_path=f_path))
172 repo_name=c.repo_name, f_path=f_path))
173
173
174 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id)
174 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.short_id)
175 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id)
175 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.short_id)
176 f_udiff = differ.get_udiff(node1, node2)
176 f_udiff = differ.get_udiff(node1, node2)
177
177
178 diff = differ.DiffProcessor(f_udiff)
178 diff = differ.DiffProcessor(f_udiff)
179
179
180 if c.action == 'download':
180 if c.action == 'download':
181 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
181 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
182 response.content_type = 'text/plain'
182 response.content_type = 'text/plain'
183 response.content_disposition = 'attachment; filename=%s' \
183 response.content_disposition = 'attachment; filename=%s' \
184 % diff_name
184 % diff_name
185 return diff.raw_diff()
185 return diff.raw_diff()
186
186
187 elif c.action == 'raw':
187 elif c.action == 'raw':
188 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
188 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
189 elif c.action == 'diff':
189 elif c.action == 'diff':
190 c.cur_diff = diff.as_html()
190 c.cur_diff = diff.as_html()
191 else:
191 else:
192 #default option
192 #default option
193 c.cur_diff = diff.as_html()
193 c.cur_diff = diff.as_html()
194
194
195 if not c.cur_diff: c.no_changes = True
195 if not c.cur_diff: c.no_changes = True
196 return render('files/file_diff.html')
196 return render('files/file_diff.html')
197
197
198 def _get_history(self, repo, node, f_path):
198 def _get_history(self, repo, node, f_path):
199 from vcs.nodes import NodeKind
199 from vcs.nodes import NodeKind
200 if not node.kind is NodeKind.FILE:
200 if not node.kind is NodeKind.FILE:
201 return []
201 return []
202 changesets = node.history
202 changesets = node.history
203 hist_l = []
203 hist_l = []
204 for chs in changesets:
204 for chs in changesets:
205 n_desc = 'r%s:%s' % (chs.revision, chs._short)
205 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
206 hist_l.append((chs._short, n_desc,))
206 hist_l.append((chs.short_id, n_desc,))
207 return hist_l
207 return hist_l
@@ -1,383 +1,383
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 from pygments.formatters import HtmlFormatter
6 from pygments.formatters import HtmlFormatter
7 from pygments import highlight as code_highlight
7 from pygments import highlight as code_highlight
8 from pylons import url, app_globals as g
8 from pylons import url, app_globals as g
9 from pylons.i18n.translation import _, ungettext
9 from pylons.i18n.translation import _, ungettext
10 from vcs.utils.annotate import annotate_highlight
10 from vcs.utils.annotate import annotate_highlight
11 from webhelpers.html import literal, HTML, escape
11 from webhelpers.html import literal, HTML, escape
12 from webhelpers.html.tools import *
12 from webhelpers.html.tools import *
13 from webhelpers.html.builder import make_tag
13 from webhelpers.html.builder import make_tag
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
17 password, textarea, title, ul, xml_declaration, radio
17 password, textarea, title, ul, xml_declaration, radio
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
19 mail_to, strip_links, strip_tags, tag_re
19 mail_to, strip_links, strip_tags, tag_re
20 from webhelpers.number import format_byte_size, format_bit_size
20 from webhelpers.number import format_byte_size, format_bit_size
21 from webhelpers.pylonslib import Flash as _Flash
21 from webhelpers.pylonslib import Flash as _Flash
22 from webhelpers.pylonslib.secure_form import secure_form
22 from webhelpers.pylonslib.secure_form import secure_form
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
25 replace_whitespace, urlify, truncate, wrap_paragraphs
25 replace_whitespace, urlify, truncate, wrap_paragraphs
26
26
27 #Custom helpers here :)
27 #Custom helpers here :)
28 class _Link(object):
28 class _Link(object):
29 '''
29 '''
30 Make a url based on label and url with help of url_for
30 Make a url based on label and url with help of url_for
31 @param label:name of link if not defined url is used
31 @param label:name of link if not defined url is used
32 @param url: the url for link
32 @param url: the url for link
33 '''
33 '''
34
34
35 def __call__(self, label='', *url_, **urlargs):
35 def __call__(self, label='', *url_, **urlargs):
36 if label is None or '':
36 if label is None or '':
37 label = url
37 label = url
38 link_fn = link_to(label, url(*url_, **urlargs))
38 link_fn = link_to(label, url(*url_, **urlargs))
39 return link_fn
39 return link_fn
40
40
41 link = _Link()
41 link = _Link()
42
42
43 class _GetError(object):
43 class _GetError(object):
44
44
45 def __call__(self, field_name, form_errors):
45 def __call__(self, field_name, form_errors):
46 tmpl = """<span class="error_msg">%s</span>"""
46 tmpl = """<span class="error_msg">%s</span>"""
47 if form_errors and form_errors.has_key(field_name):
47 if form_errors and form_errors.has_key(field_name):
48 return literal(tmpl % form_errors.get(field_name))
48 return literal(tmpl % form_errors.get(field_name))
49
49
50 get_error = _GetError()
50 get_error = _GetError()
51
51
52 def recursive_replace(str, replace=' '):
52 def recursive_replace(str, replace=' '):
53 """
53 """
54 Recursive replace of given sign to just one instance
54 Recursive replace of given sign to just one instance
55 @param str: given string
55 @param str: given string
56 @param replace:char to find and replace multiple instances
56 @param replace:char to find and replace multiple instances
57
57
58 Examples::
58 Examples::
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
60 'Mighty-Mighty-Bo-sstones'
60 'Mighty-Mighty-Bo-sstones'
61 """
61 """
62
62
63 if str.find(replace * 2) == -1:
63 if str.find(replace * 2) == -1:
64 return str
64 return str
65 else:
65 else:
66 str = str.replace(replace * 2, replace)
66 str = str.replace(replace * 2, replace)
67 return recursive_replace(str, replace)
67 return recursive_replace(str, replace)
68
68
69 class _ToolTip(object):
69 class _ToolTip(object):
70
70
71 def __call__(self, tooltip_title, trim_at=50):
71 def __call__(self, tooltip_title, trim_at=50):
72 """
72 """
73 Special function just to wrap our text into nice formatted autowrapped
73 Special function just to wrap our text into nice formatted autowrapped
74 text
74 text
75 @param tooltip_title:
75 @param tooltip_title:
76 """
76 """
77
77
78 return wrap_paragraphs(escape(tooltip_title), trim_at)\
78 return wrap_paragraphs(escape(tooltip_title), trim_at)\
79 .replace('\n', '<br/>')
79 .replace('\n', '<br/>')
80
80
81 def activate(self):
81 def activate(self):
82 """
82 """
83 Adds tooltip mechanism to the given Html all tooltips have to have
83 Adds tooltip mechanism to the given Html all tooltips have to have
84 set class tooltip and set attribute tooltip_title.
84 set class tooltip and set attribute tooltip_title.
85 Then a tooltip will be generated based on that
85 Then a tooltip will be generated based on that
86 All with yui js tooltip
86 All with yui js tooltip
87 """
87 """
88
88
89 js = '''
89 js = '''
90 YAHOO.util.Event.onDOMReady(function(){
90 YAHOO.util.Event.onDOMReady(function(){
91 function toolTipsId(){
91 function toolTipsId(){
92 var ids = [];
92 var ids = [];
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
94
94
95 for (var i = 0; i < tts.length; i++) {
95 for (var i = 0; i < tts.length; i++) {
96 //if element doesn not have and id autgenerate one for tooltip
96 //if element doesn not have and id autgenerate one for tooltip
97
97
98 if (!tts[i].id){
98 if (!tts[i].id){
99 tts[i].id='tt'+i*100;
99 tts[i].id='tt'+i*100;
100 }
100 }
101 ids.push(tts[i].id);
101 ids.push(tts[i].id);
102 }
102 }
103 return ids
103 return ids
104 };
104 };
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
106 context: toolTipsId(),
106 context: toolTipsId(),
107 monitorresize:false,
107 monitorresize:false,
108 xyoffset :[0,0],
108 xyoffset :[0,0],
109 autodismissdelay:300000,
109 autodismissdelay:300000,
110 hidedelay:5,
110 hidedelay:5,
111 showdelay:20,
111 showdelay:20,
112 });
112 });
113
113
114 //Mouse Over event disabled for new repositories since they dont
114 //Mouse Over event disabled for new repositories since they dont
115 //have last commit message
115 //have last commit message
116 myToolTips.contextMouseOverEvent.subscribe(
116 myToolTips.contextMouseOverEvent.subscribe(
117 function(type, args) {
117 function(type, args) {
118 var context = args[0];
118 var context = args[0];
119 var txt = context.getAttribute('tooltip_title');
119 var txt = context.getAttribute('tooltip_title');
120 if(txt){
120 if(txt){
121 return true;
121 return true;
122 }
122 }
123 else{
123 else{
124 return false;
124 return false;
125 }
125 }
126 });
126 });
127
127
128
128
129 // Set the text for the tooltip just before we display it. Lazy method
129 // Set the text for the tooltip just before we display it. Lazy method
130 myToolTips.contextTriggerEvent.subscribe(
130 myToolTips.contextTriggerEvent.subscribe(
131 function(type, args) {
131 function(type, args) {
132
132
133
133
134 var context = args[0];
134 var context = args[0];
135
135
136 var txt = context.getAttribute('tooltip_title');
136 var txt = context.getAttribute('tooltip_title');
137 this.cfg.setProperty("text", txt);
137 this.cfg.setProperty("text", txt);
138
138
139
139
140 // positioning of tooltip
140 // positioning of tooltip
141 var tt_w = this.element.clientWidth;
141 var tt_w = this.element.clientWidth;
142 var tt_h = this.element.clientHeight;
142 var tt_h = this.element.clientHeight;
143
143
144 var context_w = context.offsetWidth;
144 var context_w = context.offsetWidth;
145 var context_h = context.offsetHeight;
145 var context_h = context.offsetHeight;
146
146
147 var pos_x = YAHOO.util.Dom.getX(context);
147 var pos_x = YAHOO.util.Dom.getX(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
149
149
150 var display_strategy = 'top';
150 var display_strategy = 'top';
151 var xy_pos = [0,0];
151 var xy_pos = [0,0];
152 switch (display_strategy){
152 switch (display_strategy){
153
153
154 case 'top':
154 case 'top':
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
156 var cur_y = pos_y-tt_h-4;
156 var cur_y = pos_y-tt_h-4;
157 xy_pos = [cur_x,cur_y];
157 xy_pos = [cur_x,cur_y];
158 break;
158 break;
159 case 'bottom':
159 case 'bottom':
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
161 var cur_y = pos_y+context_h+4;
161 var cur_y = pos_y+context_h+4;
162 xy_pos = [cur_x,cur_y];
162 xy_pos = [cur_x,cur_y];
163 break;
163 break;
164 case 'left':
164 case 'left':
165 var cur_x = (pos_x-tt_w-4);
165 var cur_x = (pos_x-tt_w-4);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
167 xy_pos = [cur_x,cur_y];
167 xy_pos = [cur_x,cur_y];
168 break;
168 break;
169 case 'right':
169 case 'right':
170 var cur_x = (pos_x+context_w+4);
170 var cur_x = (pos_x+context_w+4);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
172 xy_pos = [cur_x,cur_y];
172 xy_pos = [cur_x,cur_y];
173 break;
173 break;
174 default:
174 default:
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
176 var cur_y = pos_y-tt_h-4;
176 var cur_y = pos_y-tt_h-4;
177 xy_pos = [cur_x,cur_y];
177 xy_pos = [cur_x,cur_y];
178 break;
178 break;
179
179
180 }
180 }
181
181
182 this.cfg.setProperty("xy",xy_pos);
182 this.cfg.setProperty("xy",xy_pos);
183
183
184 });
184 });
185
185
186 //Mouse out
186 //Mouse out
187 myToolTips.contextMouseOutEvent.subscribe(
187 myToolTips.contextMouseOutEvent.subscribe(
188 function(type, args) {
188 function(type, args) {
189 var context = args[0];
189 var context = args[0];
190
190
191 });
191 });
192 });
192 });
193 '''
193 '''
194 return literal(js)
194 return literal(js)
195
195
196 tooltip = _ToolTip()
196 tooltip = _ToolTip()
197
197
198 class _FilesBreadCrumbs(object):
198 class _FilesBreadCrumbs(object):
199
199
200 def __call__(self, repo_name, rev, paths):
200 def __call__(self, repo_name, rev, paths):
201 url_l = [link_to(repo_name, url('files_home',
201 url_l = [link_to(repo_name, url('files_home',
202 repo_name=repo_name,
202 repo_name=repo_name,
203 revision=rev, f_path=''))]
203 revision=rev, f_path=''))]
204 paths_l = paths.split('/')
204 paths_l = paths.split('/')
205
205
206 for cnt, p in enumerate(paths_l, 1):
206 for cnt, p in enumerate(paths_l, 1):
207 if p != '':
207 if p != '':
208 url_l.append(link_to(p, url('files_home',
208 url_l.append(link_to(p, url('files_home',
209 repo_name=repo_name,
209 repo_name=repo_name,
210 revision=rev,
210 revision=rev,
211 f_path='/'.join(paths_l[:cnt]))))
211 f_path='/'.join(paths_l[:cnt]))))
212
212
213 return literal('/'.join(url_l))
213 return literal('/'.join(url_l))
214
214
215 files_breadcrumbs = _FilesBreadCrumbs()
215 files_breadcrumbs = _FilesBreadCrumbs()
216 class CodeHtmlFormatter(HtmlFormatter):
216 class CodeHtmlFormatter(HtmlFormatter):
217
217
218 def wrap(self, source, outfile):
218 def wrap(self, source, outfile):
219 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
219 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
220
220
221 def _wrap_code(self, source):
221 def _wrap_code(self, source):
222 for cnt, it in enumerate(source, 1):
222 for cnt, it in enumerate(source, 1):
223 i, t = it
223 i, t = it
224 t = '<div id="#S-%s">%s</div>' % (cnt, t)
224 t = '<div id="#S-%s">%s</div>' % (cnt, t)
225 yield i, t
225 yield i, t
226 def pygmentize(filenode, **kwargs):
226 def pygmentize(filenode, **kwargs):
227 """
227 """
228 pygmentize function using pygments
228 pygmentize function using pygments
229 @param filenode:
229 @param filenode:
230 """
230 """
231 return literal(code_highlight(filenode.content,
231 return literal(code_highlight(filenode.content,
232 filenode.lexer, CodeHtmlFormatter(**kwargs)))
232 filenode.lexer, CodeHtmlFormatter(**kwargs)))
233
233
234 def pygmentize_annotation(filenode, **kwargs):
234 def pygmentize_annotation(filenode, **kwargs):
235 """
235 """
236 pygmentize function for annotation
236 pygmentize function for annotation
237 @param filenode:
237 @param filenode:
238 """
238 """
239
239
240 color_dict = {}
240 color_dict = {}
241 def gen_color():
241 def gen_color():
242 """generator for getting 10k of evenly distibuted colors using hsv color
242 """generator for getting 10k of evenly distibuted colors using hsv color
243 and golden ratio.
243 and golden ratio.
244 """
244 """
245 import colorsys
245 import colorsys
246 n = 10000
246 n = 10000
247 golden_ratio = 0.618033988749895
247 golden_ratio = 0.618033988749895
248 h = 0.22717784590367374
248 h = 0.22717784590367374
249 #generate 10k nice web friendly colors in the same order
249 #generate 10k nice web friendly colors in the same order
250 for c in xrange(n):
250 for c in xrange(n):
251 h += golden_ratio
251 h += golden_ratio
252 h %= 1
252 h %= 1
253 HSV_tuple = [h, 0.95, 0.95]
253 HSV_tuple = [h, 0.95, 0.95]
254 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
254 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
255 yield map(lambda x:str(int(x * 256)), RGB_tuple)
255 yield map(lambda x:str(int(x * 256)), RGB_tuple)
256
256
257 cgenerator = gen_color()
257 cgenerator = gen_color()
258
258
259 def get_color_string(cs):
259 def get_color_string(cs):
260 if color_dict.has_key(cs):
260 if color_dict.has_key(cs):
261 col = color_dict[cs]
261 col = color_dict[cs]
262 else:
262 else:
263 col = color_dict[cs] = cgenerator.next()
263 col = color_dict[cs] = cgenerator.next()
264 return "color: rgb(%s)! important;" % (', '.join(col))
264 return "color: rgb(%s)! important;" % (', '.join(col))
265
265
266 def url_func(changeset):
266 def url_func(changeset):
267 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
267 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
268 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
268 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
269
269
270 tooltip_html = tooltip_html % (changeset.author,
270 tooltip_html = tooltip_html % (changeset.author,
271 changeset.date,
271 changeset.date,
272 tooltip(changeset.message))
272 tooltip(changeset.message))
273 lnk_format = 'r%-5s:%s' % (changeset.revision,
273 lnk_format = 'r%-5s:%s' % (changeset.revision,
274 changeset.raw_id)
274 changeset.short_id)
275 uri = link_to(
275 uri = link_to(
276 lnk_format,
276 lnk_format,
277 url('changeset_home', repo_name=changeset.repository.name,
277 url('changeset_home', repo_name=changeset.repository.name,
278 revision=changeset.raw_id),
278 revision=changeset.short_id),
279 style=get_color_string(changeset.raw_id),
279 style=get_color_string(changeset.short_id),
280 class_='tooltip',
280 class_='tooltip',
281 tooltip_title=tooltip_html
281 tooltip_title=tooltip_html
282 )
282 )
283
283
284 uri += '\n'
284 uri += '\n'
285 return uri
285 return uri
286 return literal(annotate_highlight(filenode, url_func, **kwargs))
286 return literal(annotate_highlight(filenode, url_func, **kwargs))
287
287
288 def repo_name_slug(value):
288 def repo_name_slug(value):
289 """Return slug of name of repository
289 """Return slug of name of repository
290 This function is called on each creation/modification
290 This function is called on each creation/modification
291 of repository to prevent bad names in repo
291 of repository to prevent bad names in repo
292 """
292 """
293 slug = remove_formatting(value)
293 slug = remove_formatting(value)
294 slug = strip_tags(slug)
294 slug = strip_tags(slug)
295
295
296 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
296 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
297 slug = slug.replace(c, '-')
297 slug = slug.replace(c, '-')
298 slug = recursive_replace(slug, '-')
298 slug = recursive_replace(slug, '-')
299 slug = collapse(slug, '-')
299 slug = collapse(slug, '-')
300 return slug
300 return slug
301
301
302 def get_changeset_safe(repo, rev):
302 def get_changeset_safe(repo, rev):
303 from vcs.backends.base import BaseRepository
303 from vcs.backends.base import BaseRepository
304 from vcs.exceptions import RepositoryError
304 from vcs.exceptions import RepositoryError
305 if not isinstance(repo, BaseRepository):
305 if not isinstance(repo, BaseRepository):
306 raise Exception('You must pass an Repository '
306 raise Exception('You must pass an Repository '
307 'object as first argument got %s', type(repo))
307 'object as first argument got %s', type(repo))
308
308
309 try:
309 try:
310 cs = repo.get_changeset(rev)
310 cs = repo.get_changeset(rev)
311 except RepositoryError:
311 except RepositoryError:
312 from pylons_app.lib.utils import EmptyChangeset
312 from pylons_app.lib.utils import EmptyChangeset
313 cs = EmptyChangeset()
313 cs = EmptyChangeset()
314 return cs
314 return cs
315
315
316
316
317 flash = _Flash()
317 flash = _Flash()
318
318
319
319
320 #===============================================================================
320 #===============================================================================
321 # MERCURIAL FILTERS available via h.
321 # MERCURIAL FILTERS available via h.
322 #===============================================================================
322 #===============================================================================
323 from mercurial import util
323 from mercurial import util
324 from mercurial.templatefilters import age as _age, person as _person
324 from mercurial.templatefilters import age as _age, person as _person
325
325
326 age = lambda x:_age(x)
326 age = lambda x:_age(x)
327 capitalize = lambda x: x.capitalize()
327 capitalize = lambda x: x.capitalize()
328 date = lambda x: util.datestr(x)
328 date = lambda x: util.datestr(x)
329 email = util.email
329 email = util.email
330 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
330 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
331 person = lambda x: _person(x)
331 person = lambda x: _person(x)
332 hgdate = lambda x: "%d %d" % x
332 hgdate = lambda x: "%d %d" % x
333 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
333 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
334 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
334 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
335 localdate = lambda x: (x[0], util.makedate()[1])
335 localdate = lambda x: (x[0], util.makedate()[1])
336 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
336 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
337 rfc822date_notz = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S")
337 rfc822date_notz = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S")
338 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
338 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
339 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
339 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
340
340
341
341
342 #===============================================================================
342 #===============================================================================
343 # PERMS
343 # PERMS
344 #===============================================================================
344 #===============================================================================
345 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
345 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
346 HasRepoPermissionAny, HasRepoPermissionAll
346 HasRepoPermissionAny, HasRepoPermissionAll
347
347
348 #===============================================================================
348 #===============================================================================
349 # GRAVATAR URL
349 # GRAVATAR URL
350 #===============================================================================
350 #===============================================================================
351 import hashlib
351 import hashlib
352 import urllib
352 import urllib
353 from pylons import request
353 from pylons import request
354
354
355 def gravatar_url(email_address, size=30):
355 def gravatar_url(email_address, size=30):
356 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
356 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
357 default = 'identicon'
357 default = 'identicon'
358 baseurl_nossl = "http://www.gravatar.com/avatar/"
358 baseurl_nossl = "http://www.gravatar.com/avatar/"
359 baseurl_ssl = "https://secure.gravatar.com/avatar/"
359 baseurl_ssl = "https://secure.gravatar.com/avatar/"
360 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
360 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
361
361
362
362
363 # construct the url
363 # construct the url
364 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
364 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
365 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
365 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
366
366
367 return gravatar_url
367 return gravatar_url
368
368
369 def safe_unicode(str):
369 def safe_unicode(str):
370 """safe unicode function. In case of UnicodeDecode error we try to return
370 """safe unicode function. In case of UnicodeDecode error we try to return
371 unicode with errors replace, if this failes we return unicode with
371 unicode with errors replace, if this failes we return unicode with
372 string_escape decoding """
372 string_escape decoding """
373
373
374 try:
374 try:
375 u_str = unicode(str)
375 u_str = unicode(str)
376 except UnicodeDecodeError:
376 except UnicodeDecodeError:
377 try:
377 try:
378 u_str = unicode(str, 'utf-8', 'replace')
378 u_str = unicode(str, 'utf-8', 'replace')
379 except UnicodeDecodeError:
379 except UnicodeDecodeError:
380 #incase we have a decode error just represent as byte string
380 #incase we have a decode error just represent as byte string
381 u_str = unicode(str(str).encode('string_escape'))
381 u_str = unicode(str(str).encode('string_escape'))
382
382
383 return u_str
383 return u_str
@@ -1,169 +1,169
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for hg app
3 # Model for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for hg app
22 Model for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui
26 from mercurial import ui
27 from mercurial.hgweb.hgwebdir_mod import findrepos
27 from mercurial.hgweb.hgwebdir_mod import findrepos
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from pylons_app.lib.auth import HasRepoPermissionAny
29 from pylons_app.lib.auth import HasRepoPermissionAny
30 from pylons_app.model import meta
30 from pylons_app.model import meta
31 from pylons_app.model.db import Repository, User
31 from pylons_app.model.db import Repository, User
32 from pylons_app.lib import helpers as h
32 from pylons_app.lib import helpers as h
33 from vcs.exceptions import RepositoryError, VCSError
33 from vcs.exceptions import RepositoryError, VCSError
34 import logging
34 import logging
35 import os
35 import os
36 import sys
36 import sys
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 try:
39 try:
40 from vcs.backends.hg import MercurialRepository
40 from vcs.backends.hg import MercurialRepository
41 except ImportError:
41 except ImportError:
42 sys.stderr.write('You have to import vcs module')
42 sys.stderr.write('You have to import vcs module')
43 raise Exception('Unable to import vcs')
43 raise Exception('Unable to import vcs')
44
44
45 def _get_repos_cached_initial(app_globals, initial):
45 def _get_repos_cached_initial(app_globals, initial):
46 """return cached dict with repos
46 """return cached dict with repos
47 """
47 """
48 g = app_globals
48 g = app_globals
49 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial)
49 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial)
50
50
51 @cache_region('long_term', 'cached_repo_list')
51 @cache_region('long_term', 'cached_repo_list')
52 def _get_repos_cached():
52 def _get_repos_cached():
53 """return cached dict with repos
53 """return cached dict with repos
54 """
54 """
55 log.info('getting all repositories list')
55 log.info('getting all repositories list')
56 from pylons import app_globals as g
56 from pylons import app_globals as g
57 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
57 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
58
58
59 @cache_region('super_short_term', 'cached_repos_switcher_list')
59 @cache_region('super_short_term', 'cached_repos_switcher_list')
60 def _get_repos_switcher_cached(cached_repo_list):
60 def _get_repos_switcher_cached(cached_repo_list):
61 repos_lst = []
61 repos_lst = []
62 for repo in [x for x in cached_repo_list.values()]:
62 for repo in [x for x in cached_repo_list.values()]:
63 if HasRepoPermissionAny('repository.write', 'repository.read',
63 if HasRepoPermissionAny('repository.write', 'repository.read',
64 'repository.admin')(repo.name, 'main page check'):
64 'repository.admin')(repo.name, 'main page check'):
65 repos_lst.append((repo.name, repo.dbrepo.private,))
65 repos_lst.append((repo.name, repo.dbrepo.private,))
66
66
67 return sorted(repos_lst, key=lambda k:k[0].lower())
67 return sorted(repos_lst, key=lambda k:k[0].lower())
68
68
69 @cache_region('long_term', 'full_changelog')
69 @cache_region('long_term', 'full_changelog')
70 def _full_changelog_cached(repo_name):
70 def _full_changelog_cached(repo_name):
71 log.info('getting full changelog for %s', repo_name)
71 log.info('getting full changelog for %s', repo_name)
72 return list(reversed(list(HgModel().get_repo(repo_name))))
72 return list(reversed(list(HgModel().get_repo(repo_name))))
73
73
74 class HgModel(object):
74 class HgModel(object):
75 """Mercurial Model
75 """Mercurial Model
76 """
76 """
77
77
78 def __init__(self):
78 def __init__(self):
79 pass
79 pass
80
80
81 @staticmethod
81 @staticmethod
82 def repo_scan(repos_prefix, repos_path, baseui, initial=False):
82 def repo_scan(repos_prefix, repos_path, baseui, initial=False):
83 """
83 """
84 Listing of repositories in given path. This path should not be a
84 Listing of repositories in given path. This path should not be a
85 repository itself. Return a dictionary of repository objects
85 repository itself. Return a dictionary of repository objects
86 :param repos_path: path to directory it could take syntax with
86 :param repos_path: path to directory it could take syntax with
87 * or ** for deep recursive displaying repositories
87 * or ** for deep recursive displaying repositories
88 """
88 """
89 sa = meta.Session()
89 sa = meta.Session()
90 def check_repo_dir(path):
90 def check_repo_dir(path):
91 """Checks the repository
91 """Checks the repository
92 :param path:
92 :param path:
93 """
93 """
94 repos_path = path.split('/')
94 repos_path = path.split('/')
95 if repos_path[-1] in ['*', '**']:
95 if repos_path[-1] in ['*', '**']:
96 repos_path = repos_path[:-1]
96 repos_path = repos_path[:-1]
97 if repos_path[0] != '/':
97 if repos_path[0] != '/':
98 repos_path[0] = '/'
98 repos_path[0] = '/'
99 if not os.path.isdir(os.path.join(*repos_path)):
99 if not os.path.isdir(os.path.join(*repos_path)):
100 raise RepositoryError('Not a valid repository in %s' % path)
100 raise RepositoryError('Not a valid repository in %s' % path)
101 if not repos_path.endswith('*'):
101 if not repos_path.endswith('*'):
102 raise VCSError('You need to specify * or ** at the end of path '
102 raise VCSError('You need to specify * or ** at the end of path '
103 'for recursive scanning')
103 'for recursive scanning')
104
104
105 check_repo_dir(repos_path)
105 check_repo_dir(repos_path)
106 log.info('scanning for repositories in %s', repos_path)
106 log.info('scanning for repositories in %s', repos_path)
107 repos = findrepos([(repos_prefix, repos_path)])
107 repos = findrepos([(repos_prefix, repos_path)])
108 if not isinstance(baseui, ui.ui):
108 if not isinstance(baseui, ui.ui):
109 baseui = ui.ui()
109 baseui = ui.ui()
110
110
111 repos_list = {}
111 repos_list = {}
112 for name, path in repos:
112 for name, path in repos:
113 try:
113 try:
114 #name = name.split('/')[-1]
114 #name = name.split('/')[-1]
115 if repos_list.has_key(name):
115 if repos_list.has_key(name):
116 raise RepositoryError('Duplicate repository name %s found in'
116 raise RepositoryError('Duplicate repository name %s found in'
117 ' %s' % (name, path))
117 ' %s' % (name, path))
118 else:
118 else:
119
119
120 repos_list[name] = MercurialRepository(path, baseui=baseui)
120 repos_list[name] = MercurialRepository(path, baseui=baseui)
121 repos_list[name].name = name
121 repos_list[name].name = name
122
122
123 dbrepo = None
123 dbrepo = None
124 if not initial:
124 if not initial:
125 dbrepo = sa.query(Repository)\
125 dbrepo = sa.query(Repository)\
126 .filter(Repository.repo_name == name).scalar()
126 .filter(Repository.repo_name == name).scalar()
127
127
128 if dbrepo:
128 if dbrepo:
129 log.info('Adding db instance to cached list')
129 log.info('Adding db instance to cached list')
130 repos_list[name].dbrepo = dbrepo
130 repos_list[name].dbrepo = dbrepo
131 repos_list[name].description = dbrepo.description
131 repos_list[name].description = dbrepo.description
132 if dbrepo.user:
132 if dbrepo.user:
133 repos_list[name].contact = dbrepo.user.full_contact
133 repos_list[name].contact = dbrepo.user.full_contact
134 else:
134 else:
135 repos_list[name].contact = sa.query(User)\
135 repos_list[name].contact = sa.query(User)\
136 .filter(User.admin == True).first().full_contact
136 .filter(User.admin == True).first().full_contact
137 except OSError:
137 except OSError:
138 continue
138 continue
139 meta.Session.remove()
139 meta.Session.remove()
140 return repos_list
140 return repos_list
141
141
142 def get_repos(self):
142 def get_repos(self):
143 for name, repo in _get_repos_cached().items():
143 for name, repo in _get_repos_cached().items():
144 if repo._get_hidden():
144 if repo._get_hidden():
145 #skip hidden web repository
145 #skip hidden web repository
146 continue
146 continue
147
147
148 last_change = repo.last_change
148 last_change = repo.last_change
149 tip = h.get_changeset_safe(repo, 'tip')
149 tip = h.get_changeset_safe(repo, 'tip')
150
150
151 tmp_d = {}
151 tmp_d = {}
152 tmp_d['name'] = repo.name
152 tmp_d['name'] = repo.name
153 tmp_d['name_sort'] = tmp_d['name'].lower()
153 tmp_d['name_sort'] = tmp_d['name'].lower()
154 tmp_d['description'] = repo.description
154 tmp_d['description'] = repo.description
155 tmp_d['description_sort'] = tmp_d['description']
155 tmp_d['description_sort'] = tmp_d['description']
156 tmp_d['last_change'] = last_change
156 tmp_d['last_change'] = last_change
157 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
157 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
158 tmp_d['tip'] = tip.raw_id
158 tmp_d['tip'] = tip.short_id
159 tmp_d['tip_sort'] = tip.revision
159 tmp_d['tip_sort'] = tip.revision
160 tmp_d['rev'] = tip.revision
160 tmp_d['rev'] = tip.revision
161 tmp_d['contact'] = repo.contact
161 tmp_d['contact'] = repo.contact
162 tmp_d['contact_sort'] = tmp_d['contact']
162 tmp_d['contact_sort'] = tmp_d['contact']
163 tmp_d['repo_archives'] = list(repo._get_archives())
163 tmp_d['repo_archives'] = list(repo._get_archives())
164 tmp_d['last_msg'] = tip.message
164 tmp_d['last_msg'] = tip.message
165 tmp_d['repo'] = repo
165 tmp_d['repo'] = repo
166 yield tmp_d
166 yield tmp_d
167
167
168 def get_repo(self, repo_name):
168 def get_repo(self, repo_name):
169 return _get_repos_cached()[repo_name]
169 return _get_repos_cached()[repo_name]
@@ -1,30 +1,30
1 % if c.repo_branches:
1 % if c.repo_branches:
2 <table class="table_disp">
2 <table class="table_disp">
3 <tr>
3 <tr>
4 <th class="left">${_('date')}</th>
4 <th class="left">${_('date')}</th>
5 <th class="left">${_('revision')}</th>
5 <th class="left">${_('revision')}</th>
6 <th class="left">${_('name')}</th>
6 <th class="left">${_('name')}</th>
7 <th class="left">${_('links')}</th>
7 <th class="left">${_('links')}</th>
8 </tr>
8 </tr>
9 %for cnt,branch in enumerate(c.repo_branches.items()):
9 %for cnt,branch in enumerate(c.repo_branches.items()):
10 <tr class="parity${cnt%2}">
10 <tr class="parity${cnt%2}">
11 <td>${h.age(branch[1]._ctx.date())}</td>
11 <td>${h.age(branch[1]._ctx.date())}</td>
12 <td>r${branch[1].revision}:${branch[1].raw_id}</td>
12 <td>r${branch[1].revision}:${branch[1].short_id}</td>
13 <td>
13 <td>
14 <span class="logtags">
14 <span class="logtags">
15 <span class="branchtag">${h.link_to(branch[0],
15 <span class="branchtag">${h.link_to(branch[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].raw_id))}</span>
16 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}</span>
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="nowrap">
19 <td class="nowrap">
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].raw_id))}
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}
21 |
21 |
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id))}
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=branch[1].short_id))}
23 </td>
23 </td>
24 </tr>
24 </tr>
25 %endfor
25 %endfor
26 </table>
26 </table>
27 %else:
27 %else:
28 ${_('There are no branches yet')}
28 ${_('There are no branches yet')}
29 %endif
29 %endif
30
30
@@ -1,117 +1,117
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('Changelog - %s') % c.repo_name}
6 ${_('Changelog - %s') % c.repo_name}
7 </%def>
7 </%def>
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(u'Home',h.url('/'))}
10 ${h.link_to(u'Home',h.url('/'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
15 </%def>
15 </%def>
16
16
17 <%def name="page_nav()">
17 <%def name="page_nav()">
18 ${self.menu('changelog')}
18 ${self.menu('changelog')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <!-- box / title -->
23 <!-- box / title -->
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27 <div class="table">
27 <div class="table">
28 % if c.pagination:
28 % if c.pagination:
29 <div id="graph">
29 <div id="graph">
30 <div id="graph_nodes">
30 <div id="graph_nodes">
31 <canvas id="graph_canvas"></canvas>
31 <canvas id="graph_canvas"></canvas>
32 </div>
32 </div>
33 <div id="graph_content">
33 <div id="graph_content">
34 <div class="container_header">
34 <div class="container_header">
35
35
36 ${h.form(h.url.current(),method='get')}
36 ${h.form(h.url.current(),method='get')}
37 <div class="info_box">
37 <div class="info_box">
38 <span>${_('Show')}:</span>
38 <span>${_('Show')}:</span>
39 ${h.text('size',size=1,value=c.size)}
39 ${h.text('size',size=1,value=c.size)}
40 <span>${_('revisions')}</span>
40 <span>${_('revisions')}</span>
41 ${h.submit('set',_('set'))}
41 ${h.submit('set',_('set'))}
42 </div>
42 </div>
43 ${h.end_form()}
43 ${h.end_form()}
44
44
45 </div>
45 </div>
46 %for cnt,cs in enumerate(c.pagination):
46 %for cnt,cs in enumerate(c.pagination):
47 <div id="chg_${cnt+1}" class="container">
47 <div id="chg_${cnt+1}" class="container">
48 <div class="left">
48 <div class="left">
49 <div class="date">${_('commit')} ${cs.revision}: ${cs.raw_id}@${cs.date}</div>
49 <div class="date">${_('commit')} ${cs.revision}: ${cs.short_id}@${cs.date}</div>
50 <span class="logtags">
50 <span class="logtags">
51 <span class="branchtag">${cs.branch}</span>
51 <span class="branchtag">${cs.branch}</span>
52 %for tag in cs.tags:
52 %for tag in cs.tags:
53 <span class="tagtag">${tag}</span>
53 <span class="tagtag">${tag}</span>
54 %endfor
54 %endfor
55 </span>
55 </span>
56 <div class="author">
56 <div class="author">
57 <div class="gravatar">
57 <div class="gravatar">
58 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
58 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
59 </div>
59 </div>
60 <span>${h.person(cs.author)}</span><br/>
60 <span>${h.person(cs.author)}</span><br/>
61 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
61 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
62 </div>
62 </div>
63 <div class="message">
63 <div class="message">
64 ${h.link_to(h.wrap_paragraphs(cs.message),
64 ${h.link_to(h.wrap_paragraphs(cs.message),
65 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
65 h.url('changeset_home',repo_name=c.repo_name,revision=cs.short_id))}
66 </div>
66 </div>
67 </div>
67 </div>
68 <div class="right">
68 <div class="right">
69 <div class="changes">
69 <div class="changes">
70 <span class="removed" title="${_('removed')}">${len(cs.removed)}</span>
70 <span class="removed" title="${_('removed')}">${len(cs.removed)}</span>
71 <span class="changed" title="${_('changed')}">${len(cs.changed)}</span>
71 <span class="changed" title="${_('changed')}">${len(cs.changed)}</span>
72 <span class="added" title="${_('added')}">${len(cs.added)}</span>
72 <span class="added" title="${_('added')}">${len(cs.added)}</span>
73 </div>
73 </div>
74 %if len(cs.parents)>1:
74 %if len(cs.parents)>1:
75 <div class="merge">
75 <div class="merge">
76 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
76 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
77 </div>
77 </div>
78 %endif
78 %endif
79 %for p_cs in reversed(cs.parents):
79 %for p_cs in reversed(cs.parents):
80 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.raw_id,
80 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.short_id,
81 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
81 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.short_id),title=p_cs.message)}
82 </div>
82 </div>
83 %endfor
83 %endfor
84 </div>
84 </div>
85 </div>
85 </div>
86
86
87 %endfor
87 %endfor
88 <div class="pagination-wh pagination-left">
88 <div class="pagination-wh pagination-left">
89 ${c.pagination.pager('$link_previous ~2~ $link_next')}
89 ${c.pagination.pager('$link_previous ~2~ $link_next')}
90 </div>
90 </div>
91 </div>
91 </div>
92 </div>
92 </div>
93
93
94 <script type="text/javascript" src="/js/graph.js"></script>
94 <script type="text/javascript" src="/js/graph.js"></script>
95 <script type="text/javascript">
95 <script type="text/javascript">
96 YAHOO.util.Event.onDOMReady(function(){
96 YAHOO.util.Event.onDOMReady(function(){
97 function set_canvas() {
97 function set_canvas() {
98 var c = document.getElementById('graph_nodes');
98 var c = document.getElementById('graph_nodes');
99 var t = document.getElementById('graph_content');
99 var t = document.getElementById('graph_content');
100 canvas = document.getElementById('graph_canvas');
100 canvas = document.getElementById('graph_canvas');
101 var div_h = t.clientHeight;
101 var div_h = t.clientHeight;
102 c.style.height=div_h+'px';
102 c.style.height=div_h+'px';
103 canvas.setAttribute('height',div_h);
103 canvas.setAttribute('height',div_h);
104 canvas.setAttribute('width',160);
104 canvas.setAttribute('width',160);
105 };
105 };
106 set_canvas();
106 set_canvas();
107 var jsdata = ${c.jsdata|n};
107 var jsdata = ${c.jsdata|n};
108 var r = new BranchRenderer();
108 var r = new BranchRenderer();
109 r.render(jsdata);
109 r.render(jsdata);
110 });
110 });
111 </script>
111 </script>
112 %else:
112 %else:
113 ${_('There are no changes yet')}
113 ${_('There are no changes yet')}
114 %endif
114 %endif
115 </div>
115 </div>
116 </div>
116 </div>
117 </%def> No newline at end of file
117 </%def>
@@ -1,106 +1,106
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Changeset')}
4 ${_('Changeset')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.raw_id}
12 ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.short_id}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('changelog')}
16 ${self.menu('changelog')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <div class="table">
25 <div class="table">
26 <div id="body" class="diffblock">
26 <div id="body" class="diffblock">
27 <div class="code-header">
27 <div class="code-header">
28 <div>
28 <div>
29 ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.raw_id}
29 ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.short_id}
30 &raquo; <span>${h.link_to(_('raw diff'),
30 &raquo; <span>${h.link_to(_('raw diff'),
31 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
31 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.short_id,diff='show'))}</span>
32 &raquo; <span>${h.link_to(_('download diff'),
32 &raquo; <span>${h.link_to(_('download diff'),
33 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
33 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.short_id,diff='download'))}</span>
34 </div>
34 </div>
35 </div>
35 </div>
36 </div>
36 </div>
37
37
38 <div id="changeset_content">
38 <div id="changeset_content">
39 <div class="container">
39 <div class="container">
40 <div class="left">
40 <div class="left">
41 <div class="date">${_('Date')}: ${c.changeset.date}</div>
41 <div class="date">${_('Date')}: ${c.changeset.date}</div>
42 <div class="author">${_('Author')}: ${c.changeset.author}</div>
42 <div class="author">${_('Author')}: ${c.changeset.author}</div>
43 <div class="message">${h.wrap_paragraphs(c.changeset.message)}</div>
43 <div class="message">${h.wrap_paragraphs(c.changeset.message)}</div>
44 </div>
44 </div>
45 <div class="right">
45 <div class="right">
46 <span class="logtags">
46 <span class="logtags">
47 <span class="branchtag">${c.changeset.branch}</span>
47 <span class="branchtag">${c.changeset.branch}</span>
48 %for tag in c.changeset.tags:
48 %for tag in c.changeset.tags:
49 <span class="tagtag">${tag}</span>
49 <span class="tagtag">${tag}</span>
50 %endfor
50 %endfor
51 </span>
51 </span>
52 %if len(c.changeset.parents)>1:
52 %if len(c.changeset.parents)>1:
53 <div class="merge">
53 <div class="merge">
54 ${_('merge')}
54 ${_('merge')}
55 <img alt="merge" src="/images/icons/arrow_join.png">
55 <img alt="merge" src="/images/icons/arrow_join.png">
56 </div>
56 </div>
57 %endif
57 %endif
58 %for p_cs in reversed(c.changeset.parents):
58 %for p_cs in reversed(c.changeset.parents):
59 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.raw_id,
59 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.short_id,
60 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
60 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.short_id),title=p_cs.message)}
61 </div>
61 </div>
62 %endfor
62 %endfor
63 </div>
63 </div>
64 </div>
64 </div>
65 <span style="font-size:1.1em;font-weight: bold">${_('Files affected')}</span>
65 <span style="font-size:1.1em;font-weight: bold">${_('Files affected')}</span>
66 <div class="cs_files">
66 <div class="cs_files">
67 %for change,filenode,diff,cs1,cs2 in c.changes:
67 %for change,filenode,diff,cs1,cs2 in c.changes:
68 <div class="cs_${change}">${h.link_to(filenode.path,h.url.current(anchor='CHANGE-%s'%filenode.path))}</div>
68 <div class="cs_${change}">${h.link_to(filenode.path,h.url.current(anchor='CHANGE-%s'%filenode.path))}</div>
69 %endfor
69 %endfor
70 </div>
70 </div>
71 </div>
71 </div>
72
72
73 %for change,filenode,diff,cs1,cs2 in c.changes:
73 %for change,filenode,diff,cs1,cs2 in c.changes:
74 %if change !='removed':
74 %if change !='removed':
75 <div style="clear:both;height:10px"></div>
75 <div style="clear:both;height:10px"></div>
76 <div id="body" class="diffblock">
76 <div id="body" class="diffblock">
77 <div id="${'CHANGE-%s'%filenode.path}" class="code-header">
77 <div id="${'CHANGE-%s'%filenode.path}" class="code-header">
78 <div>
78 <div>
79 <span>
79 <span>
80 ${h.link_to_if(change!='removed',filenode.path,h.url('files_home',repo_name=c.repo_name,
80 ${h.link_to_if(change!='removed',filenode.path,h.url('files_home',repo_name=c.repo_name,
81 revision=filenode.changeset.raw_id,f_path=filenode.path))}
81 revision=filenode.changeset.short_id,f_path=filenode.path))}
82 </span>
82 </span>
83 %if 1:
83 %if 1:
84 &raquo; <span>${h.link_to(_('diff'),
84 &raquo; <span>${h.link_to(_('diff'),
85 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='diff'))}</span>
85 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='diff'))}</span>
86 &raquo; <span>${h.link_to(_('raw diff'),
86 &raquo; <span>${h.link_to(_('raw diff'),
87 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='raw'))}</span>
87 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='raw'))}</span>
88 &raquo; <span>${h.link_to(_('download diff'),
88 &raquo; <span>${h.link_to(_('download diff'),
89 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='download'))}</span>
89 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='download'))}</span>
90 %endif
90 %endif
91 </div>
91 </div>
92 </div>
92 </div>
93 <div class="code-body">
93 <div class="code-body">
94 %if diff:
94 %if diff:
95 ${diff|n}
95 ${diff|n}
96 %else:
96 %else:
97 ${_('No changes in this file')}
97 ${_('No changes in this file')}
98 %endif
98 %endif
99 </div>
99 </div>
100 </div>
100 </div>
101 %endif
101 %endif
102 %endfor
102 %endfor
103 </div>
103 </div>
104 </div>
104 </div>
105
105
106 </%def> No newline at end of file
106 </%def>
@@ -1,61 +1,61
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('File annotate')}
4 ${_('File annotate')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('annotate')} @ R${c.rev_nr}:${c.cur_rev}
12 ${_('annotate')} @ R${c.rev_nr}:${c.cur_rev}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('files')}
16 ${self.menu('files')}
17 </%def>
17 </%def>
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24 <div class="table">
24 <div class="table">
25 <div id="files_data">
25 <div id="files_data">
26 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.file.path)}</h3>
26 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.file.path)}</h3>
27 <dl class="overview">
27 <dl class="overview">
28 <dt>${_('Last revision')}</dt>
28 <dt>${_('Last revision')}</dt>
29 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,c.file.last_changeset._short),
29 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,c.file.last_changeset.short_id),
30 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset._short,f_path=c.f_path))} </dd>
30 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset.short_id,f_path=c.f_path))} </dd>
31 <dt>${_('Size')}</dt>
31 <dt>${_('Size')}</dt>
32 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
32 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
33 <dt>${_('Mimetype')}</dt>
33 <dt>${_('Mimetype')}</dt>
34 <dd>${c.file.mimetype}</dd>
34 <dd>${c.file.mimetype}</dd>
35 <dt>${_('Options')}</dt>
35 <dt>${_('Options')}</dt>
36 <dd>${h.link_to(_('show source'),
36 <dd>${h.link_to(_('show source'),
37 h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
37 h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
38 / ${h.link_to(_('show as raw'),
38 / ${h.link_to(_('show as raw'),
39 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
39 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
40 / ${h.link_to(_('download as raw'),
40 / ${h.link_to(_('download as raw'),
41 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
41 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
42 </dd>
42 </dd>
43 </dl>
43 </dl>
44 <div id="body" class="codeblock">
44 <div id="body" class="codeblock">
45 <div class="code-header">
45 <div class="code-header">
46 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${c.file.last_changeset._short}</div>
46 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${c.file.last_changeset.short_id}</div>
47 <div class="commit">"${c.file_msg}"</div>
47 <div class="commit">"${c.file_msg}"</div>
48 </div>
48 </div>
49 <div class="code-body">
49 <div class="code-body">
50 % if c.file.size < c.file_size_limit:
50 % if c.file.size < c.file_size_limit:
51 ${h.pygmentize_annotation(c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
51 ${h.pygmentize_annotation(c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
52 %else:
52 %else:
53 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
53 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
55 %endif
55 %endif
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59 </div>
59 </div>
60 </div>
60 </div>
61 </%def> No newline at end of file
61 </%def>
@@ -1,57 +1,57
1 <dl>
1 <dl>
2 <dt>${_('Last revision')}</dt>
2 <dt>${_('Last revision')}</dt>
3 <dd>
3 <dd>
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,c.files_list.last_changeset._short),
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,c.files_list.last_changeset.short_id),
5 h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset._short,f_path=c.f_path))}
5 h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset.short_id,f_path=c.f_path))}
6 </dd>
6 </dd>
7 <dt>${_('Size')}</dt>
7 <dt>${_('Size')}</dt>
8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
9 <dt>${_('Mimetype')}</dt>
9 <dt>${_('Mimetype')}</dt>
10 <dd>${c.files_list.mimetype}</dd>
10 <dd>${c.files_list.mimetype}</dd>
11 <dt>${_('Options')}</dt>
11 <dt>${_('Options')}</dt>
12 <dd>${h.link_to(_('show annotation'),
12 <dd>${h.link_to(_('show annotation'),
13 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
13 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
14 / ${h.link_to(_('show as raw'),
14 / ${h.link_to(_('show as raw'),
15 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
15 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
16 / ${h.link_to(_('download as raw'),
16 / ${h.link_to(_('download as raw'),
17 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
17 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
18 </dd>
18 </dd>
19 <dt>${_('History')}</dt>
19 <dt>${_('History')}</dt>
20 <dd>
20 <dd>
21 <div>
21 <div>
22 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
22 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
23 ${h.hidden('diff2',c.files_list.last_changeset._short)}
23 ${h.hidden('diff2',c.files_list.last_changeset.short_id)}
24 ${h.select('diff1',c.files_list.last_changeset._short,c.file_history)}
24 ${h.select('diff1',c.files_list.last_changeset.short_id,c.file_history)}
25 ${h.submit('diff','diff to revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
25 ${h.submit('diff','diff to revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
26 ${h.submit('show_rev','show at revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
26 ${h.submit('show_rev','show at revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
27 ${h.end_form()}
27 ${h.end_form()}
28 </div>
28 </div>
29 </dd>
29 </dd>
30 </dl>
30 </dl>
31
31
32
32
33 <div id="body" class="codeblock">
33 <div id="body" class="codeblock">
34 <div class="code-header">
34 <div class="code-header">
35 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${c.files_list.last_changeset._short}</div>
35 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${c.files_list.last_changeset.short_id}</div>
36 <div class="commit">"${c.files_list.last_changeset.message}"</div>
36 <div class="commit">"${c.files_list.last_changeset.message}"</div>
37 </div>
37 </div>
38 <div class="code-body">
38 <div class="code-body">
39 % if c.files_list.size < c.file_size_limit:
39 % if c.files_list.size < c.file_size_limit:
40 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
40 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
41 %else:
41 %else:
42 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
42 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
43 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
43 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
44 %endif
44 %endif
45 </div>
45 </div>
46 </div>
46 </div>
47
47
48 <script type="text/javascript">
48 <script type="text/javascript">
49 YAHOO.util.Event.onDOMReady(function(){
49 YAHOO.util.Event.onDOMReady(function(){
50 YAHOO.util.Event.addListener('show_rev','click',function(e){
50 YAHOO.util.Event.addListener('show_rev','click',function(e){
51 YAHOO.util.Event.preventDefault(e);
51 YAHOO.util.Event.preventDefault(e);
52 var cs = YAHOO.util.Dom.get('diff1').value;
52 var cs = YAHOO.util.Dom.get('diff1').value;
53 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
53 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
54 window.location = url;
54 window.location = url;
55 });
55 });
56 });
56 });
57 </script> No newline at end of file
57 </script>
@@ -1,63 +1,63
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 % if c.repo_changesets:
2 % if c.repo_changesets:
3 <table>
3 <table>
4 <tr>
4 <tr>
5 <th class="left">${_('date')}</th>
5 <th class="left">${_('date')}</th>
6 <th class="left">${_('author')}</th>
6 <th class="left">${_('author')}</th>
7 <th class="left">${_('revision')}</th>
7 <th class="left">${_('revision')}</th>
8 <th class="left">${_('commit message')}</th>
8 <th class="left">${_('commit message')}</th>
9 <th class="left">${_('branch')}</th>
9 <th class="left">${_('branch')}</th>
10 <th class="left">${_('tags')}</th>
10 <th class="left">${_('tags')}</th>
11 <th class="left">${_('links')}</th>
11 <th class="left">${_('links')}</th>
12
12
13 </tr>
13 </tr>
14 %for cnt,cs in enumerate(c.repo_changesets):
14 %for cnt,cs in enumerate(c.repo_changesets):
15 <tr class="parity${cnt%2}">
15 <tr class="parity${cnt%2}">
16 <td>${h.age(cs._ctx.date())} - ${h.rfc822date_notz(cs._ctx.date())} </td>
16 <td>${h.age(cs._ctx.date())} - ${h.rfc822date_notz(cs._ctx.date())} </td>
17 <td title="${cs.author}">${h.person(cs.author)}</td>
17 <td title="${cs.author}">${h.person(cs.author)}</td>
18 <td>r${cs.revision}:${cs.raw_id}</td>
18 <td>r${cs.revision}:${cs.short_id}</td>
19 <td>
19 <td>
20 ${h.link_to(h.truncate(cs.message,60),
20 ${h.link_to(h.truncate(cs.message,60),
21 h.url('changeset_home',repo_name=c.repo_name,revision=cs._short),
21 h.url('changeset_home',repo_name=c.repo_name,revision=cs.short_id),
22 title=cs.message)}
22 title=cs.message)}
23 </td>
23 </td>
24 <td>
24 <td>
25 <span class="logtags">
25 <span class="logtags">
26 <span class="branchtag">${cs.branch}</span>
26 <span class="branchtag">${cs.branch}</span>
27 </span>
27 </span>
28 </td>
28 </td>
29 <td>
29 <td>
30 <span class="logtags">
30 <span class="logtags">
31 %for tag in cs.tags:
31 %for tag in cs.tags:
32 <span class="tagtag">${tag}</span>
32 <span class="tagtag">${tag}</span>
33 %endfor
33 %endfor
34 </span>
34 </span>
35 </td>
35 </td>
36 <td class="nowrap">
36 <td class="nowrap">
37 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
37 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.short_id))}
38 |
38 |
39 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
39 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.short_id))}
40 </td>
40 </td>
41 </tr>
41 </tr>
42 %endfor
42 %endfor
43
43
44 </table>
44 </table>
45
45
46 <script type="text/javascript">
46 <script type="text/javascript">
47 var data_div = 'shortlog_data';
47 var data_div = 'shortlog_data';
48 YAHOO.util.Event.onDOMReady(function(){
48 YAHOO.util.Event.onDOMReady(function(){
49 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
49 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
50 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
50 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
51 </script>
51 </script>
52
52
53 <div class="pagination-wh pagination-left">
53 <div class="pagination-wh pagination-left">
54 ${c.repo_changesets.pager('$link_previous ~2~ $link_next',
54 ${c.repo_changesets.pager('$link_previous ~2~ $link_next',
55 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
55 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
56 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
56 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
57 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
57 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
58 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
58 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
59 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
59 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
60 </div>
60 </div>
61 %else:
61 %else:
62 ${_('There are no changes yet')}
62 ${_('There are no changes yet')}
63 %endif
63 %endif
@@ -1,29 +1,29
1 %if c.repo_tags:
1 %if c.repo_tags:
2 <table>
2 <table>
3 <tr>
3 <tr>
4 <th class="left">${_('date')}</th>
4 <th class="left">${_('date')}</th>
5 <th class="left">${_('revision')}</th>
5 <th class="left">${_('revision')}</th>
6 <th class="left">${_('name')}</th>
6 <th class="left">${_('name')}</th>
7 <th class="left">${_('links')}</th>
7 <th class="left">${_('links')}</th>
8 </tr>
8 </tr>
9 %for cnt,tag in enumerate(c.repo_tags.items()):
9 %for cnt,tag in enumerate(c.repo_tags.items()):
10 <tr class="parity${cnt%2}">
10 <tr class="parity${cnt%2}">
11 <td>${h.age(tag[1]._ctx.date())}</td>
11 <td>${h.age(tag[1]._ctx.date())}</td>
12 <td>r${tag[1].revision}:${tag[1].raw_id}</td>
12 <td>r${tag[1].revision}:${tag[1].short_id}</td>
13 <td>
13 <td>
14 <span class="logtags">
14 <span class="logtags">
15 <span class="tagtag">${h.link_to(tag[0],
15 <span class="tagtag">${h.link_to(tag[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].raw_id))}</span>
16 h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}</span>
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="nowrap">
19 <td class="nowrap">
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}
21 |
21 |
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=tag[1].short_id))}
23 </td>
23 </td>
24 </tr>
24 </tr>
25 %endfor
25 %endfor
26 </table>
26 </table>
27 %else:
27 %else:
28 ${_('There are no tags yet')}
28 ${_('There are no tags yet')}
29 %endif No newline at end of file
29 %endif
@@ -1,49 +1,49
1 from pylons_app import get_version
1 from pylons_app import get_version
2 try:
2 try:
3 from setuptools import setup, find_packages
3 from setuptools import setup, find_packages
4 except ImportError:
4 except ImportError:
5 from ez_setup import use_setuptools
5 from ez_setup import use_setuptools
6 use_setuptools()
6 use_setuptools()
7 from setuptools import setup, find_packages
7 from setuptools import setup, find_packages
8
8
9 setup(
9 setup(
10 name='HgApp-%s' % get_version(),
10 name='HgApp-%s' % get_version(),
11 version=get_version(),
11 version=get_version(),
12 description='Mercurial repository serving and browsing app',
12 description='Mercurial repository serving and browsing app',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
14 license='BSD',
14 license='BSD',
15 author='marcin kuzminski',
15 author='marcin kuzminski',
16 author_email='marcin@python-works.com',
16 author_email='marcin@python-works.com',
17 url='http://hg.python-works.com',
17 url='http://hg.python-works.com',
18 install_requires=[
18 install_requires=[
19 "Pylons>=1.0.0",
19 "Pylons>=1.0.0",
20 "SQLAlchemy>=0.6",
20 "SQLAlchemy>=0.6",
21 "babel",
21 "babel",
22 "Mako>=0.3.2",
22 "Mako>=0.3.2",
23 "vcs>=0.1.5",
23 "vcs>=0.1.6",
24 "pygments>=1.3.0",
24 "pygments>=1.3.0",
25 "mercurial>=1.6",
25 "mercurial>=1.6",
26 "pysqlite",
26 "pysqlite",
27 "whoosh==1.0.0b17",
27 "whoosh==1.0.0b17",
28 "py-bcrypt",
28 "py-bcrypt",
29 "celery",
29 "celery",
30 ],
30 ],
31 setup_requires=["PasteScript>=1.6.3"],
31 setup_requires=["PasteScript>=1.6.3"],
32 packages=find_packages(exclude=['ez_setup']),
32 packages=find_packages(exclude=['ez_setup']),
33 include_package_data=True,
33 include_package_data=True,
34 test_suite='nose.collector',
34 test_suite='nose.collector',
35 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
35 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
36 message_extractors={'pylons_app': [
36 message_extractors={'pylons_app': [
37 ('**.py', 'python', None),
37 ('**.py', 'python', None),
38 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
38 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
39 ('public/**', 'ignore', None)]},
39 ('public/**', 'ignore', None)]},
40 zip_safe=False,
40 zip_safe=False,
41 paster_plugins=['PasteScript', 'Pylons'],
41 paster_plugins=['PasteScript', 'Pylons'],
42 entry_points="""
42 entry_points="""
43 [paste.app_factory]
43 [paste.app_factory]
44 main = pylons_app.config.middleware:make_app
44 main = pylons_app.config.middleware:make_app
45
45
46 [paste.app_install]
46 [paste.app_install]
47 main = pylons.util:PylonsInstaller
47 main = pylons.util:PylonsInstaller
48 """,
48 """,
49 )
49 )
General Comments 0
You need to be logged in to leave comments. Login now