##// END OF EJS Templates
created pull-request overview
marcink -
r2395:b262e349 codereview
parent child Browse files
Show More
@@ -0,0 +1,23 b''
1 ## Changesets table !
2 <div class="container">
3 <table class="compare_view_commits noborder">
4 %if not c.cs_ranges:
5 <tr><td>${_('No changesets')}</td></tr>
6 %else:
7 %for cnt, cs in enumerate(c.cs_ranges):
8 <tr>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
10 <td>
11 %if cs.raw_id in c.statuses:
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
13 %endif
14 </td>
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
16 <td><div class="author">${h.person(cs.author)}</div></td>
17 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
18 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
19 </tr>
20 %endfor
21 %endif
22 </table>
23 </div> No newline at end of file
@@ -1,142 +1,143 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.compare
3 rhodecode.controllers.compare
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 compare controller for pylons showoing differences between two
6 compare controller for pylons showoing differences between two
7 repos, branches, bookmarks or tips
7 repos, branches, bookmarks or tips
8
8
9 :created_on: May 6, 2012
9 :created_on: May 6, 2012
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import binascii
28 import binascii
29
29
30 from webob.exc import HTTPNotFound
30 from webob.exc import HTTPNotFound
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib import diffs
37 from rhodecode.lib import diffs
38
38
39 from rhodecode.model.db import Repository
39 from rhodecode.model.db import Repository
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class CompareController(BaseRepoController):
44 class CompareController(BaseRepoController):
45
45
46 @LoginRequired()
46 @LoginRequired()
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 'repository.admin')
48 'repository.admin')
49 def __before__(self):
49 def __before__(self):
50 super(CompareController, self).__before__()
50 super(CompareController, self).__before__()
51
51
52 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
52 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
53 from mercurial import discovery
53 from mercurial import discovery
54 other = org_repo._repo
54 other = org_repo._repo
55 repo = other_repo._repo
55 repo = other_repo._repo
56 tip = other[org_ref[1]]
56 tip = other[org_ref[1]]
57 log.debug('Doing discovery for %s@%s vs %s@%s' % (
57 log.debug('Doing discovery for %s@%s vs %s@%s' % (
58 org_repo, org_ref, other_repo, other_ref)
58 org_repo, org_ref, other_repo, other_ref)
59 )
59 )
60 log.debug('Filter heads are %s[%s]' % (tip, org_ref[1]))
60 log.debug('Filter heads are %s[%s]' % (tip, org_ref[1]))
61 tmp = discovery.findcommonincoming(
61 tmp = discovery.findcommonincoming(
62 repo=repo, # other_repo we check for incoming
62 repo=repo, # other_repo we check for incoming
63 remote=other, # org_repo source for incoming
63 remote=other, # org_repo source for incoming
64 heads=[tip.node()],
64 heads=[tip.node()],
65 force=False
65 force=False
66 )
66 )
67 return tmp
67 return tmp
68
68
69 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref, tmp):
69 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref, tmp):
70 changesets = []
70 changesets = []
71 #case two independent repos
71 #case two independent repos
72 if org_repo != other_repo:
72 if org_repo != other_repo:
73 common, incoming, rheads = tmp
73 common, incoming, rheads = tmp
74
74
75 if not incoming:
75 if not incoming:
76 revs = []
76 revs = []
77 else:
77 else:
78 revs = org_repo._repo.changelog.findmissing(common, rheads)
78 revs = org_repo._repo.changelog.findmissing(common, rheads)
79
79
80 for cs in reversed(map(binascii.hexlify, revs)):
80 for cs in reversed(map(binascii.hexlify, revs)):
81 changesets.append(org_repo.get_changeset(cs))
81 changesets.append(org_repo.get_changeset(cs))
82 else:
82 else:
83 revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1],
83 revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1],
84 other_ref[1])]
84 other_ref[1])]
85 from mercurial import scmutil
85 from mercurial import scmutil
86 out = scmutil.revrange(org_repo._repo, revs)
86 out = scmutil.revrange(org_repo._repo, revs)
87 for cs in reversed(out):
87 for cs in reversed(out):
88 changesets.append(org_repo.get_changeset(cs))
88 changesets.append(org_repo.get_changeset(cs))
89
89
90 return changesets
90 return changesets
91
91
92 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
92 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
93
93
94 org_repo = c.rhodecode_db_repo.repo_name
94 org_repo = c.rhodecode_db_repo.repo_name
95 org_ref = (org_ref_type, org_ref)
95 org_ref = (org_ref_type, org_ref)
96 other_ref = (other_ref_type, other_ref)
96 other_ref = (other_ref_type, other_ref)
97 other_repo = request.GET.get('repo', org_repo)
97 other_repo = request.GET.get('repo', org_repo)
98
98
99 c.swap_url = h.url('compare_url', repo_name=other_repo,
99 c.swap_url = h.url('compare_url', repo_name=other_repo,
100 org_ref_type=other_ref[0], org_ref=other_ref[1],
100 org_ref_type=other_ref[0], org_ref=other_ref[1],
101 other_ref_type=org_ref[0], other_ref=org_ref[1],
101 other_ref_type=org_ref[0], other_ref=org_ref[1],
102 repo=org_repo)
102 repo=org_repo)
103
103
104 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
104 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
105 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
105 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
106
106
107 if c.org_repo is None or c.other_repo is None:
107 if c.org_repo is None or c.other_repo is None:
108 log.error('Could not found repo %s or %s' % (org_repo, other_repo))
108 log.error('Could not found repo %s or %s' % (org_repo, other_repo))
109 raise HTTPNotFound
109 raise HTTPNotFound
110
110
111 discovery_data = self._get_discovery(org_repo.scm_instance,
111 discovery_data = self._get_discovery(org_repo.scm_instance,
112 org_ref,
112 org_ref,
113 other_repo.scm_instance,
113 other_repo.scm_instance,
114 other_ref)
114 other_ref)
115 c.cs_ranges = self._get_changesets(org_repo.scm_instance,
115 c.cs_ranges = self._get_changesets(org_repo.scm_instance,
116 org_ref,
116 org_ref,
117 other_repo.scm_instance,
117 other_repo.scm_instance,
118 other_ref,
118 other_ref,
119 discovery_data)
119 discovery_data)
120
120
121 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
121 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
122 c.cs_ranges])
122 c.cs_ranges])
123
123 if request.environ.get('HTTP_X_PARTIAL_XHR'):
124 return render('compare/compare_cs.html')
124
125
125 c.org_ref = org_ref[1]
126 c.org_ref = org_ref[1]
126 c.other_ref = other_ref[1]
127 c.other_ref = other_ref[1]
127 # diff needs to have swapped org with other to generate proper diff
128 # diff needs to have swapped org with other to generate proper diff
128 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
129 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
129 discovery_data)
130 discovery_data)
130 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
131 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
131 _parsed = diff_processor.prepare()
132 _parsed = diff_processor.prepare()
132
133
133 c.files = []
134 c.files = []
134 c.changes = {}
135 c.changes = {}
135
136
136 for f in _parsed:
137 for f in _parsed:
137 fid = h.FID('', f['filename'])
138 fid = h.FID('', f['filename'])
138 c.files.append([fid, f['operation'], f['filename'], f['stats']])
139 c.files.append([fid, f['operation'], f['filename'], f['stats']])
139 diff = diff_processor.as_html(enable_comments=False, diff_lines=[f])
140 diff = diff_processor.as_html(enable_comments=False, diff_lines=[f])
140 c.changes[fid] = [f['operation'], f['filename'], diff]
141 c.changes[fid] = [f['operation'], f['filename'], diff]
141
142
142 return render('compare/compare_diff.html')
143 return render('compare/compare_diff.html')
@@ -1,65 +1,90 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.pullrequests
3 rhodecode.controllers.pullrequests
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull requests controller for rhodecode for initializing pull requests
6 pull requests controller for rhodecode for initializing pull requests
7
7
8 :created_on: May 7, 2012
8 :created_on: May 7, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 from pylons import request, response, session, tmpl_context as c, url
28 from pylons import request, response, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.lib.base import BaseRepoController, render
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from webob.exc import HTTPNotFound
34 from rhodecode.model.db import User
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class PullrequestsController(BaseRepoController):
39 class PullrequestsController(BaseRepoController):
40
40
41 @LoginRequired()
41 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
43 'repository.admin')
44 def __before__(self):
44 def __before__(self):
45 super(PullrequestsController, self).__before__()
45 super(PullrequestsController, self).__before__()
46
46
47 def _get_repo_refs(self,repo):
47 def _get_repo_refs(self, repo):
48 hist_l = []
48 hist_l = []
49
49
50 branches_group = ([(k, k) for k in repo.branches.keys()], _("Branches"))
50 branches_group = ([('branch:' + k, k) for k in repo.branches.keys()],
51 bookmarks_group = ([(k, k) for k in repo.bookmarks.keys()], _("Bookmarks"))
51 _("Branches"))
52 tags_group = ([(k, k) for k in repo.tags.keys()], _("Tags"))
52 bookmarks_group = ([('book:' + k, k) for k in repo.bookmarks.keys()],
53 _("Bookmarks"))
54 tags_group = ([('tag:' + k, k) for k in repo.tags.keys()],
55 _("Tags"))
53
56
54 hist_l.append(bookmarks_group)
57 hist_l.append(bookmarks_group)
55 hist_l.append(branches_group)
58 hist_l.append(branches_group)
56 hist_l.append(tags_group)
59 hist_l.append(tags_group)
57
60
58 return hist_l
61 return hist_l
59
62
60 def index(self):
63 def index(self):
64 org_repo = c.rhodecode_db_repo
61 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
65 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
62 c.sources = []
66 c.org_repos = []
63 c.sources.append('%s/%s' % (c.rhodecode_db_repo.user.username,
67 c.other_repos = []
64 c.repo_name))
68 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
69 org_repo.user.username, c.repo_name))
70 )
71
72 c.other_refs = c.org_refs
73 c.other_repos.extend(c.org_repos)
74
75 #gather forks and add to this list
76 for fork in org_repo.forks:
77 c.other_repos.append((fork.repo_name, '%s/%s' % (
78 fork.user.username, fork.repo_name))
79 )
80 #add parents of this fork also
81 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
82 org_repo.parent.user.username,
83 org_repo.parent.repo_name))
84 )
85
86 #TODO: maybe the owner should be default ?
87 c.review_members = []
88 c.available_members = [(x.user_id, x.username) for x in
89 User.query().filter(User.username != 'default').all()]
65 return render('/pullrequests/pullrequest.html')
90 return render('/pullrequests/pullrequest.html')
@@ -1,1448 +1,1462 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from pylons.i18n.translation import lazy_ugettext as _
39 from pylons.i18n.translation import lazy_ugettext as _
40
40
41 from rhodecode.lib.vcs import get_backend
41 from rhodecode.lib.vcs import get_backend
42 from rhodecode.lib.vcs.utils.helpers import get_scm
42 from rhodecode.lib.vcs.utils.helpers import get_scm
43 from rhodecode.lib.vcs.exceptions import VCSError
43 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.utils.lazy import LazyProperty
44 from rhodecode.lib.vcs.utils.lazy import LazyProperty
45
45
46 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
46 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
47 safe_unicode
47 safe_unicode
48 from rhodecode.lib.compat import json
48 from rhodecode.lib.compat import json
49 from rhodecode.lib.caching_query import FromCache
49 from rhodecode.lib.caching_query import FromCache
50
50
51 from rhodecode.model.meta import Base, Session
51 from rhodecode.model.meta import Base, Session
52
52
53
53
54 URL_SEP = '/'
54 URL_SEP = '/'
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 #==============================================================================
57 #==============================================================================
58 # BASE CLASSES
58 # BASE CLASSES
59 #==============================================================================
59 #==============================================================================
60
60
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62
62
63
63
64 class ModelSerializer(json.JSONEncoder):
64 class ModelSerializer(json.JSONEncoder):
65 """
65 """
66 Simple Serializer for JSON,
66 Simple Serializer for JSON,
67
67
68 usage::
68 usage::
69
69
70 to make object customized for serialization implement a __json__
70 to make object customized for serialization implement a __json__
71 method that will return a dict for serialization into json
71 method that will return a dict for serialization into json
72
72
73 example::
73 example::
74
74
75 class Task(object):
75 class Task(object):
76
76
77 def __init__(self, name, value):
77 def __init__(self, name, value):
78 self.name = name
78 self.name = name
79 self.value = value
79 self.value = value
80
80
81 def __json__(self):
81 def __json__(self):
82 return dict(name=self.name,
82 return dict(name=self.name,
83 value=self.value)
83 value=self.value)
84
84
85 """
85 """
86
86
87 def default(self, obj):
87 def default(self, obj):
88
88
89 if hasattr(obj, '__json__'):
89 if hasattr(obj, '__json__'):
90 return obj.__json__()
90 return obj.__json__()
91 else:
91 else:
92 return json.JSONEncoder.default(self, obj)
92 return json.JSONEncoder.default(self, obj)
93
93
94
94
95 class BaseModel(object):
95 class BaseModel(object):
96 """
96 """
97 Base Model for all classess
97 Base Model for all classess
98 """
98 """
99
99
100 @classmethod
100 @classmethod
101 def _get_keys(cls):
101 def _get_keys(cls):
102 """return column names for this model """
102 """return column names for this model """
103 return class_mapper(cls).c.keys()
103 return class_mapper(cls).c.keys()
104
104
105 def get_dict(self):
105 def get_dict(self):
106 """
106 """
107 return dict with keys and values corresponding
107 return dict with keys and values corresponding
108 to this model data """
108 to this model data """
109
109
110 d = {}
110 d = {}
111 for k in self._get_keys():
111 for k in self._get_keys():
112 d[k] = getattr(self, k)
112 d[k] = getattr(self, k)
113
113
114 # also use __json__() if present to get additional fields
114 # also use __json__() if present to get additional fields
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
116 d[k] = val
116 d[k] = val
117 return d
117 return d
118
118
119 def get_appstruct(self):
119 def get_appstruct(self):
120 """return list with keys and values tupples corresponding
120 """return list with keys and values tupples corresponding
121 to this model data """
121 to this model data """
122
122
123 l = []
123 l = []
124 for k in self._get_keys():
124 for k in self._get_keys():
125 l.append((k, getattr(self, k),))
125 l.append((k, getattr(self, k),))
126 return l
126 return l
127
127
128 def populate_obj(self, populate_dict):
128 def populate_obj(self, populate_dict):
129 """populate model with data from given populate_dict"""
129 """populate model with data from given populate_dict"""
130
130
131 for k in self._get_keys():
131 for k in self._get_keys():
132 if k in populate_dict:
132 if k in populate_dict:
133 setattr(self, k, populate_dict[k])
133 setattr(self, k, populate_dict[k])
134
134
135 @classmethod
135 @classmethod
136 def query(cls):
136 def query(cls):
137 return Session.query(cls)
137 return Session.query(cls)
138
138
139 @classmethod
139 @classmethod
140 def get(cls, id_):
140 def get(cls, id_):
141 if id_:
141 if id_:
142 return cls.query().get(id_)
142 return cls.query().get(id_)
143
143
144 @classmethod
144 @classmethod
145 def getAll(cls):
145 def getAll(cls):
146 return cls.query().all()
146 return cls.query().all()
147
147
148 @classmethod
148 @classmethod
149 def delete(cls, id_):
149 def delete(cls, id_):
150 obj = cls.query().get(id_)
150 obj = cls.query().get(id_)
151 Session.delete(obj)
151 Session.delete(obj)
152
152
153 def __repr__(self):
153 def __repr__(self):
154 if hasattr(self, '__unicode__'):
154 if hasattr(self, '__unicode__'):
155 # python repr needs to return str
155 # python repr needs to return str
156 return safe_str(self.__unicode__())
156 return safe_str(self.__unicode__())
157 return '<DB:%s>' % (self.__class__.__name__)
157 return '<DB:%s>' % (self.__class__.__name__)
158
158
159
159
160 class RhodeCodeSetting(Base, BaseModel):
160 class RhodeCodeSetting(Base, BaseModel):
161 __tablename__ = 'rhodecode_settings'
161 __tablename__ = 'rhodecode_settings'
162 __table_args__ = (
162 __table_args__ = (
163 UniqueConstraint('app_settings_name'),
163 UniqueConstraint('app_settings_name'),
164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
165 'mysql_charset': 'utf8'}
165 'mysql_charset': 'utf8'}
166 )
166 )
167 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
168 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
168 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
170
170
171 def __init__(self, k='', v=''):
171 def __init__(self, k='', v=''):
172 self.app_settings_name = k
172 self.app_settings_name = k
173 self.app_settings_value = v
173 self.app_settings_value = v
174
174
175 @validates('_app_settings_value')
175 @validates('_app_settings_value')
176 def validate_settings_value(self, key, val):
176 def validate_settings_value(self, key, val):
177 assert type(val) == unicode
177 assert type(val) == unicode
178 return val
178 return val
179
179
180 @hybrid_property
180 @hybrid_property
181 def app_settings_value(self):
181 def app_settings_value(self):
182 v = self._app_settings_value
182 v = self._app_settings_value
183 if self.app_settings_name == 'ldap_active':
183 if self.app_settings_name == 'ldap_active':
184 v = str2bool(v)
184 v = str2bool(v)
185 return v
185 return v
186
186
187 @app_settings_value.setter
187 @app_settings_value.setter
188 def app_settings_value(self, val):
188 def app_settings_value(self, val):
189 """
189 """
190 Setter that will always make sure we use unicode in app_settings_value
190 Setter that will always make sure we use unicode in app_settings_value
191
191
192 :param val:
192 :param val:
193 """
193 """
194 self._app_settings_value = safe_unicode(val)
194 self._app_settings_value = safe_unicode(val)
195
195
196 def __unicode__(self):
196 def __unicode__(self):
197 return u"<%s('%s:%s')>" % (
197 return u"<%s('%s:%s')>" % (
198 self.__class__.__name__,
198 self.__class__.__name__,
199 self.app_settings_name, self.app_settings_value
199 self.app_settings_name, self.app_settings_value
200 )
200 )
201
201
202 @classmethod
202 @classmethod
203 def get_by_name(cls, ldap_key):
203 def get_by_name(cls, ldap_key):
204 return cls.query()\
204 return cls.query()\
205 .filter(cls.app_settings_name == ldap_key).scalar()
205 .filter(cls.app_settings_name == ldap_key).scalar()
206
206
207 @classmethod
207 @classmethod
208 def get_app_settings(cls, cache=False):
208 def get_app_settings(cls, cache=False):
209
209
210 ret = cls.query()
210 ret = cls.query()
211
211
212 if cache:
212 if cache:
213 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
213 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
214
214
215 if not ret:
215 if not ret:
216 raise Exception('Could not get application settings !')
216 raise Exception('Could not get application settings !')
217 settings = {}
217 settings = {}
218 for each in ret:
218 for each in ret:
219 settings['rhodecode_' + each.app_settings_name] = \
219 settings['rhodecode_' + each.app_settings_name] = \
220 each.app_settings_value
220 each.app_settings_value
221
221
222 return settings
222 return settings
223
223
224 @classmethod
224 @classmethod
225 def get_ldap_settings(cls, cache=False):
225 def get_ldap_settings(cls, cache=False):
226 ret = cls.query()\
226 ret = cls.query()\
227 .filter(cls.app_settings_name.startswith('ldap_')).all()
227 .filter(cls.app_settings_name.startswith('ldap_')).all()
228 fd = {}
228 fd = {}
229 for row in ret:
229 for row in ret:
230 fd.update({row.app_settings_name: row.app_settings_value})
230 fd.update({row.app_settings_name: row.app_settings_value})
231
231
232 return fd
232 return fd
233
233
234
234
235 class RhodeCodeUi(Base, BaseModel):
235 class RhodeCodeUi(Base, BaseModel):
236 __tablename__ = 'rhodecode_ui'
236 __tablename__ = 'rhodecode_ui'
237 __table_args__ = (
237 __table_args__ = (
238 UniqueConstraint('ui_key'),
238 UniqueConstraint('ui_key'),
239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
240 'mysql_charset': 'utf8'}
240 'mysql_charset': 'utf8'}
241 )
241 )
242
242
243 HOOK_UPDATE = 'changegroup.update'
243 HOOK_UPDATE = 'changegroup.update'
244 HOOK_REPO_SIZE = 'changegroup.repo_size'
244 HOOK_REPO_SIZE = 'changegroup.repo_size'
245 HOOK_PUSH = 'pretxnchangegroup.push_logger'
245 HOOK_PUSH = 'pretxnchangegroup.push_logger'
246 HOOK_PULL = 'preoutgoing.pull_logger'
246 HOOK_PULL = 'preoutgoing.pull_logger'
247
247
248 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
248 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
252 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
252 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
253
253
254 @classmethod
254 @classmethod
255 def get_by_key(cls, key):
255 def get_by_key(cls, key):
256 return cls.query().filter(cls.ui_key == key)
256 return cls.query().filter(cls.ui_key == key)
257
257
258 @classmethod
258 @classmethod
259 def get_builtin_hooks(cls):
259 def get_builtin_hooks(cls):
260 q = cls.query()
260 q = cls.query()
261 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
261 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
262 cls.HOOK_REPO_SIZE,
262 cls.HOOK_REPO_SIZE,
263 cls.HOOK_PUSH, cls.HOOK_PULL]))
263 cls.HOOK_PUSH, cls.HOOK_PULL]))
264 return q.all()
264 return q.all()
265
265
266 @classmethod
266 @classmethod
267 def get_custom_hooks(cls):
267 def get_custom_hooks(cls):
268 q = cls.query()
268 q = cls.query()
269 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
269 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
270 cls.HOOK_REPO_SIZE,
270 cls.HOOK_REPO_SIZE,
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
272 q = q.filter(cls.ui_section == 'hooks')
272 q = q.filter(cls.ui_section == 'hooks')
273 return q.all()
273 return q.all()
274
274
275 @classmethod
275 @classmethod
276 def create_or_update_hook(cls, key, val):
276 def create_or_update_hook(cls, key, val):
277 new_ui = cls.get_by_key(key).scalar() or cls()
277 new_ui = cls.get_by_key(key).scalar() or cls()
278 new_ui.ui_section = 'hooks'
278 new_ui.ui_section = 'hooks'
279 new_ui.ui_active = True
279 new_ui.ui_active = True
280 new_ui.ui_key = key
280 new_ui.ui_key = key
281 new_ui.ui_value = val
281 new_ui.ui_value = val
282
282
283 Session.add(new_ui)
283 Session.add(new_ui)
284
284
285
285
286 class User(Base, BaseModel):
286 class User(Base, BaseModel):
287 __tablename__ = 'users'
287 __tablename__ = 'users'
288 __table_args__ = (
288 __table_args__ = (
289 UniqueConstraint('username'), UniqueConstraint('email'),
289 UniqueConstraint('username'), UniqueConstraint('email'),
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
291 'mysql_charset': 'utf8'}
291 'mysql_charset': 'utf8'}
292 )
292 )
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304
304
305 user_log = relationship('UserLog', cascade='all')
305 user_log = relationship('UserLog', cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
307
307
308 repositories = relationship('Repository')
308 repositories = relationship('Repository')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
312
312
313 group_member = relationship('UsersGroupMember', cascade='all')
313 group_member = relationship('UsersGroupMember', cascade='all')
314
314
315 notifications = relationship('UserNotification', cascade='all')
315 notifications = relationship('UserNotification', cascade='all')
316 # notifications assigned to this user
316 # notifications assigned to this user
317 user_created_notifications = relationship('Notification', cascade='all')
317 user_created_notifications = relationship('Notification', cascade='all')
318 # comments created by this user
318 # comments created by this user
319 user_comments = relationship('ChangesetComment', cascade='all')
319 user_comments = relationship('ChangesetComment', cascade='all')
320
320
321 @hybrid_property
321 @hybrid_property
322 def email(self):
322 def email(self):
323 return self._email
323 return self._email
324
324
325 @email.setter
325 @email.setter
326 def email(self, val):
326 def email(self, val):
327 self._email = val.lower() if val else None
327 self._email = val.lower() if val else None
328
328
329 @property
329 @property
330 def full_name(self):
330 def full_name(self):
331 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
332
332
333 @property
333 @property
334 def full_name_or_username(self):
334 def full_name_or_username(self):
335 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
336 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
337
337
338 @property
338 @property
339 def full_contact(self):
339 def full_contact(self):
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
341
341
342 @property
342 @property
343 def short_contact(self):
343 def short_contact(self):
344 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
345
345
346 @property
346 @property
347 def is_admin(self):
347 def is_admin(self):
348 return self.admin
348 return self.admin
349
349
350 def __unicode__(self):
350 def __unicode__(self):
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
352 self.user_id, self.username)
352 self.user_id, self.username)
353
353
354 @classmethod
354 @classmethod
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
356 if case_insensitive:
356 if case_insensitive:
357 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
358 else:
358 else:
359 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
360
360
361 if cache:
361 if cache:
362 q = q.options(FromCache(
362 q = q.options(FromCache(
363 "sql_cache_short",
363 "sql_cache_short",
364 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
365 )
365 )
366 )
366 )
367 return q.scalar()
367 return q.scalar()
368
368
369 @classmethod
369 @classmethod
370 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
371 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
372
372
373 if cache:
373 if cache:
374 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
375 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
376 return q.scalar()
376 return q.scalar()
377
377
378 @classmethod
378 @classmethod
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
380 if case_insensitive:
380 if case_insensitive:
381 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
382 else:
382 else:
383 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
384
384
385 if cache:
385 if cache:
386 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
387 "get_email_key_%s" % email))
387 "get_email_key_%s" % email))
388
388
389 ret = q.scalar()
389 ret = q.scalar()
390 if ret is None:
390 if ret is None:
391 q = UserEmailMap.query()
391 q = UserEmailMap.query()
392 # try fetching in alternate email map
392 # try fetching in alternate email map
393 if case_insensitive:
393 if case_insensitive:
394 q = q.filter(UserEmailMap.email.ilike(email))
394 q = q.filter(UserEmailMap.email.ilike(email))
395 else:
395 else:
396 q = q.filter(UserEmailMap.email == email)
396 q = q.filter(UserEmailMap.email == email)
397 q = q.options(joinedload(UserEmailMap.user))
397 q = q.options(joinedload(UserEmailMap.user))
398 if cache:
398 if cache:
399 q = q.options(FromCache("sql_cache_short",
399 q = q.options(FromCache("sql_cache_short",
400 "get_email_map_key_%s" % email))
400 "get_email_map_key_%s" % email))
401 ret = getattr(q.scalar(), 'user', None)
401 ret = getattr(q.scalar(), 'user', None)
402
402
403 return ret
403 return ret
404
404
405 def update_lastlogin(self):
405 def update_lastlogin(self):
406 """Update user lastlogin"""
406 """Update user lastlogin"""
407 self.last_login = datetime.datetime.now()
407 self.last_login = datetime.datetime.now()
408 Session.add(self)
408 Session.add(self)
409 log.debug('updated user %s lastlogin' % self.username)
409 log.debug('updated user %s lastlogin' % self.username)
410
410
411 def __json__(self):
411 def __json__(self):
412 return dict(
412 return dict(
413 user_id=self.user_id,
413 user_id=self.user_id,
414 first_name=self.name,
414 first_name=self.name,
415 last_name=self.lastname,
415 last_name=self.lastname,
416 email=self.email,
416 email=self.email,
417 full_name=self.full_name,
417 full_name=self.full_name,
418 full_name_or_username=self.full_name_or_username,
418 full_name_or_username=self.full_name_or_username,
419 short_contact=self.short_contact,
419 short_contact=self.short_contact,
420 full_contact=self.full_contact
420 full_contact=self.full_contact
421 )
421 )
422
422
423
423
424 class UserEmailMap(Base, BaseModel):
424 class UserEmailMap(Base, BaseModel):
425 __tablename__ = 'user_email_map'
425 __tablename__ = 'user_email_map'
426 __table_args__ = (
426 __table_args__ = (
427 UniqueConstraint('email'),
427 UniqueConstraint('email'),
428 {'extend_existing': True, 'mysql_engine':'InnoDB',
428 {'extend_existing': True, 'mysql_engine':'InnoDB',
429 'mysql_charset': 'utf8'}
429 'mysql_charset': 'utf8'}
430 )
430 )
431 __mapper_args__ = {}
431 __mapper_args__ = {}
432
432
433 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
433 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
434 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
435 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
435 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
436
436
437 user = relationship('User')
437 user = relationship('User')
438
438
439 @validates('_email')
439 @validates('_email')
440 def validate_email(self, key, email):
440 def validate_email(self, key, email):
441 # check if this email is not main one
441 # check if this email is not main one
442 main_email = Session.query(User).filter(User.email == email).scalar()
442 main_email = Session.query(User).filter(User.email == email).scalar()
443 if main_email is not None:
443 if main_email is not None:
444 raise AttributeError('email %s is present is user table' % email)
444 raise AttributeError('email %s is present is user table' % email)
445 return email
445 return email
446
446
447 @hybrid_property
447 @hybrid_property
448 def email(self):
448 def email(self):
449 return self._email
449 return self._email
450
450
451 @email.setter
451 @email.setter
452 def email(self, val):
452 def email(self, val):
453 self._email = val.lower() if val else None
453 self._email = val.lower() if val else None
454
454
455
455
456 class UserLog(Base, BaseModel):
456 class UserLog(Base, BaseModel):
457 __tablename__ = 'user_logs'
457 __tablename__ = 'user_logs'
458 __table_args__ = (
458 __table_args__ = (
459 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 {'extend_existing': True, 'mysql_engine': 'InnoDB',
460 'mysql_charset': 'utf8'},
460 'mysql_charset': 'utf8'},
461 )
461 )
462 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
462 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
463 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
464 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
464 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
465 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
465 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
467 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
467 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
468 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
468 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
469
469
470 @property
470 @property
471 def action_as_day(self):
471 def action_as_day(self):
472 return datetime.date(*self.action_date.timetuple()[:3])
472 return datetime.date(*self.action_date.timetuple()[:3])
473
473
474 user = relationship('User')
474 user = relationship('User')
475 repository = relationship('Repository', cascade='')
475 repository = relationship('Repository', cascade='')
476
476
477
477
478 class UsersGroup(Base, BaseModel):
478 class UsersGroup(Base, BaseModel):
479 __tablename__ = 'users_groups'
479 __tablename__ = 'users_groups'
480 __table_args__ = (
480 __table_args__ = (
481 {'extend_existing': True, 'mysql_engine': 'InnoDB',
481 {'extend_existing': True, 'mysql_engine': 'InnoDB',
482 'mysql_charset': 'utf8'},
482 'mysql_charset': 'utf8'},
483 )
483 )
484
484
485 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
485 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
486 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
486 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
487 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
487 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
488
488
489 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
489 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
490 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
490 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
491 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
491 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
492
492
493 def __unicode__(self):
493 def __unicode__(self):
494 return u'<userGroup(%s)>' % (self.users_group_name)
494 return u'<userGroup(%s)>' % (self.users_group_name)
495
495
496 @classmethod
496 @classmethod
497 def get_by_group_name(cls, group_name, cache=False,
497 def get_by_group_name(cls, group_name, cache=False,
498 case_insensitive=False):
498 case_insensitive=False):
499 if case_insensitive:
499 if case_insensitive:
500 q = cls.query().filter(cls.users_group_name.ilike(group_name))
500 q = cls.query().filter(cls.users_group_name.ilike(group_name))
501 else:
501 else:
502 q = cls.query().filter(cls.users_group_name == group_name)
502 q = cls.query().filter(cls.users_group_name == group_name)
503 if cache:
503 if cache:
504 q = q.options(FromCache(
504 q = q.options(FromCache(
505 "sql_cache_short",
505 "sql_cache_short",
506 "get_user_%s" % _hash_key(group_name)
506 "get_user_%s" % _hash_key(group_name)
507 )
507 )
508 )
508 )
509 return q.scalar()
509 return q.scalar()
510
510
511 @classmethod
511 @classmethod
512 def get(cls, users_group_id, cache=False):
512 def get(cls, users_group_id, cache=False):
513 users_group = cls.query()
513 users_group = cls.query()
514 if cache:
514 if cache:
515 users_group = users_group.options(FromCache("sql_cache_short",
515 users_group = users_group.options(FromCache("sql_cache_short",
516 "get_users_group_%s" % users_group_id))
516 "get_users_group_%s" % users_group_id))
517 return users_group.get(users_group_id)
517 return users_group.get(users_group_id)
518
518
519
519
520 class UsersGroupMember(Base, BaseModel):
520 class UsersGroupMember(Base, BaseModel):
521 __tablename__ = 'users_groups_members'
521 __tablename__ = 'users_groups_members'
522 __table_args__ = (
522 __table_args__ = (
523 {'extend_existing': True, 'mysql_engine': 'InnoDB',
523 {'extend_existing': True, 'mysql_engine': 'InnoDB',
524 'mysql_charset': 'utf8'},
524 'mysql_charset': 'utf8'},
525 )
525 )
526
526
527 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
527 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
528 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
528 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
529 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
529 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
530
530
531 user = relationship('User', lazy='joined')
531 user = relationship('User', lazy='joined')
532 users_group = relationship('UsersGroup')
532 users_group = relationship('UsersGroup')
533
533
534 def __init__(self, gr_id='', u_id=''):
534 def __init__(self, gr_id='', u_id=''):
535 self.users_group_id = gr_id
535 self.users_group_id = gr_id
536 self.user_id = u_id
536 self.user_id = u_id
537
537
538
538
539 class Repository(Base, BaseModel):
539 class Repository(Base, BaseModel):
540 __tablename__ = 'repositories'
540 __tablename__ = 'repositories'
541 __table_args__ = (
541 __table_args__ = (
542 UniqueConstraint('repo_name'),
542 UniqueConstraint('repo_name'),
543 {'extend_existing': True, 'mysql_engine': 'InnoDB',
543 {'extend_existing': True, 'mysql_engine': 'InnoDB',
544 'mysql_charset': 'utf8'},
544 'mysql_charset': 'utf8'},
545 )
545 )
546
546
547 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
547 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
548 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
548 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
549 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
549 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
550 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
550 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
551 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
551 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
552 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
552 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
553 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
553 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
554 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
554 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
555 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
555 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
556 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
556 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
557
557
558 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
558 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
560
560
561 user = relationship('User')
561 user = relationship('User')
562 fork = relationship('Repository', remote_side=repo_id)
562 fork = relationship('Repository', remote_side=repo_id)
563 group = relationship('RepoGroup')
563 group = relationship('RepoGroup')
564 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
564 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
565 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
565 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
566 stats = relationship('Statistics', cascade='all', uselist=False)
566 stats = relationship('Statistics', cascade='all', uselist=False)
567
567
568 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
568 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
569
569
570 logs = relationship('UserLog')
570 logs = relationship('UserLog')
571 comments = relationship('ChangesetComment')
571 comments = relationship('ChangesetComment')
572
572
573 def __unicode__(self):
573 def __unicode__(self):
574 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
574 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
575 self.repo_name)
575 self.repo_name)
576
576
577 @classmethod
577 @classmethod
578 def url_sep(cls):
578 def url_sep(cls):
579 return URL_SEP
579 return URL_SEP
580
580
581 @classmethod
581 @classmethod
582 def get_by_repo_name(cls, repo_name):
582 def get_by_repo_name(cls, repo_name):
583 q = Session.query(cls).filter(cls.repo_name == repo_name)
583 q = Session.query(cls).filter(cls.repo_name == repo_name)
584 q = q.options(joinedload(Repository.fork))\
584 q = q.options(joinedload(Repository.fork))\
585 .options(joinedload(Repository.user))\
585 .options(joinedload(Repository.user))\
586 .options(joinedload(Repository.group))
586 .options(joinedload(Repository.group))
587 return q.scalar()
587 return q.scalar()
588
588
589 @classmethod
589 @classmethod
590 def get_repo_forks(cls, repo_id):
590 def get_repo_forks(cls, repo_id):
591 return cls.query().filter(Repository.fork_id == repo_id)
591 return cls.query().filter(Repository.fork_id == repo_id)
592
592
593 @classmethod
593 @classmethod
594 def base_path(cls):
594 def base_path(cls):
595 """
595 """
596 Returns base path when all repos are stored
596 Returns base path when all repos are stored
597
597
598 :param cls:
598 :param cls:
599 """
599 """
600 q = Session.query(RhodeCodeUi)\
600 q = Session.query(RhodeCodeUi)\
601 .filter(RhodeCodeUi.ui_key == cls.url_sep())
601 .filter(RhodeCodeUi.ui_key == cls.url_sep())
602 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
602 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
603 return q.one().ui_value
603 return q.one().ui_value
604
604
605 @property
605 @property
606 def forks(self):
607 """
608 Return forks of this repo
609 """
610 return Repository.get_repo_forks(self.repo_id)
611
612 @property
613 def parent(self):
614 """
615 Returns fork parent
616 """
617 return self.fork
618
619 @property
606 def just_name(self):
620 def just_name(self):
607 return self.repo_name.split(Repository.url_sep())[-1]
621 return self.repo_name.split(Repository.url_sep())[-1]
608
622
609 @property
623 @property
610 def groups_with_parents(self):
624 def groups_with_parents(self):
611 groups = []
625 groups = []
612 if self.group is None:
626 if self.group is None:
613 return groups
627 return groups
614
628
615 cur_gr = self.group
629 cur_gr = self.group
616 groups.insert(0, cur_gr)
630 groups.insert(0, cur_gr)
617 while 1:
631 while 1:
618 gr = getattr(cur_gr, 'parent_group', None)
632 gr = getattr(cur_gr, 'parent_group', None)
619 cur_gr = cur_gr.parent_group
633 cur_gr = cur_gr.parent_group
620 if gr is None:
634 if gr is None:
621 break
635 break
622 groups.insert(0, gr)
636 groups.insert(0, gr)
623
637
624 return groups
638 return groups
625
639
626 @property
640 @property
627 def groups_and_repo(self):
641 def groups_and_repo(self):
628 return self.groups_with_parents, self.just_name
642 return self.groups_with_parents, self.just_name
629
643
630 @LazyProperty
644 @LazyProperty
631 def repo_path(self):
645 def repo_path(self):
632 """
646 """
633 Returns base full path for that repository means where it actually
647 Returns base full path for that repository means where it actually
634 exists on a filesystem
648 exists on a filesystem
635 """
649 """
636 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
650 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
637 Repository.url_sep())
651 Repository.url_sep())
638 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
652 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
639 return q.one().ui_value
653 return q.one().ui_value
640
654
641 @property
655 @property
642 def repo_full_path(self):
656 def repo_full_path(self):
643 p = [self.repo_path]
657 p = [self.repo_path]
644 # we need to split the name by / since this is how we store the
658 # we need to split the name by / since this is how we store the
645 # names in the database, but that eventually needs to be converted
659 # names in the database, but that eventually needs to be converted
646 # into a valid system path
660 # into a valid system path
647 p += self.repo_name.split(Repository.url_sep())
661 p += self.repo_name.split(Repository.url_sep())
648 return os.path.join(*p)
662 return os.path.join(*p)
649
663
650 def get_new_name(self, repo_name):
664 def get_new_name(self, repo_name):
651 """
665 """
652 returns new full repository name based on assigned group and new new
666 returns new full repository name based on assigned group and new new
653
667
654 :param group_name:
668 :param group_name:
655 """
669 """
656 path_prefix = self.group.full_path_splitted if self.group else []
670 path_prefix = self.group.full_path_splitted if self.group else []
657 return Repository.url_sep().join(path_prefix + [repo_name])
671 return Repository.url_sep().join(path_prefix + [repo_name])
658
672
659 @property
673 @property
660 def _ui(self):
674 def _ui(self):
661 """
675 """
662 Creates an db based ui object for this repository
676 Creates an db based ui object for this repository
663 """
677 """
664 from mercurial import ui
678 from mercurial import ui
665 from mercurial import config
679 from mercurial import config
666 baseui = ui.ui()
680 baseui = ui.ui()
667
681
668 #clean the baseui object
682 #clean the baseui object
669 baseui._ocfg = config.config()
683 baseui._ocfg = config.config()
670 baseui._ucfg = config.config()
684 baseui._ucfg = config.config()
671 baseui._tcfg = config.config()
685 baseui._tcfg = config.config()
672
686
673 ret = RhodeCodeUi.query()\
687 ret = RhodeCodeUi.query()\
674 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
688 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
675
689
676 hg_ui = ret
690 hg_ui = ret
677 for ui_ in hg_ui:
691 for ui_ in hg_ui:
678 if ui_.ui_active:
692 if ui_.ui_active:
679 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
693 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
680 ui_.ui_key, ui_.ui_value)
694 ui_.ui_key, ui_.ui_value)
681 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
695 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
682
696
683 return baseui
697 return baseui
684
698
685 @classmethod
699 @classmethod
686 def is_valid(cls, repo_name):
700 def is_valid(cls, repo_name):
687 """
701 """
688 returns True if given repo name is a valid filesystem repository
702 returns True if given repo name is a valid filesystem repository
689
703
690 :param cls:
704 :param cls:
691 :param repo_name:
705 :param repo_name:
692 """
706 """
693 from rhodecode.lib.utils import is_valid_repo
707 from rhodecode.lib.utils import is_valid_repo
694
708
695 return is_valid_repo(repo_name, cls.base_path())
709 return is_valid_repo(repo_name, cls.base_path())
696
710
697 #==========================================================================
711 #==========================================================================
698 # SCM PROPERTIES
712 # SCM PROPERTIES
699 #==========================================================================
713 #==========================================================================
700
714
701 def get_changeset(self, rev=None):
715 def get_changeset(self, rev=None):
702 return get_changeset_safe(self.scm_instance, rev)
716 return get_changeset_safe(self.scm_instance, rev)
703
717
704 @property
718 @property
705 def tip(self):
719 def tip(self):
706 return self.get_changeset('tip')
720 return self.get_changeset('tip')
707
721
708 @property
722 @property
709 def author(self):
723 def author(self):
710 return self.tip.author
724 return self.tip.author
711
725
712 @property
726 @property
713 def last_change(self):
727 def last_change(self):
714 return self.scm_instance.last_change
728 return self.scm_instance.last_change
715
729
716 def comments(self, revisions=None):
730 def comments(self, revisions=None):
717 """
731 """
718 Returns comments for this repository grouped by revisions
732 Returns comments for this repository grouped by revisions
719
733
720 :param revisions: filter query by revisions only
734 :param revisions: filter query by revisions only
721 """
735 """
722 cmts = ChangesetComment.query()\
736 cmts = ChangesetComment.query()\
723 .filter(ChangesetComment.repo == self)
737 .filter(ChangesetComment.repo == self)
724 if revisions:
738 if revisions:
725 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
739 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
726 grouped = defaultdict(list)
740 grouped = defaultdict(list)
727 for cmt in cmts.all():
741 for cmt in cmts.all():
728 grouped[cmt.revision].append(cmt)
742 grouped[cmt.revision].append(cmt)
729 return grouped
743 return grouped
730
744
731 def statuses(self, revisions=None):
745 def statuses(self, revisions=None):
732 """
746 """
733 Returns statuses for this repository
747 Returns statuses for this repository
734
748
735 :param revisions: list of revisions to get statuses for
749 :param revisions: list of revisions to get statuses for
736 :type revisions: list
750 :type revisions: list
737 """
751 """
738
752
739 statuses = ChangesetStatus.query()\
753 statuses = ChangesetStatus.query()\
740 .filter(ChangesetStatus.repo == self)\
754 .filter(ChangesetStatus.repo == self)\
741 .filter(ChangesetStatus.version == 0)
755 .filter(ChangesetStatus.version == 0)
742 if revisions:
756 if revisions:
743 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
757 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
744 grouped = {}
758 grouped = {}
745 for stat in statuses.all():
759 for stat in statuses.all():
746 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
760 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
747 return grouped
761 return grouped
748
762
749 #==========================================================================
763 #==========================================================================
750 # SCM CACHE INSTANCE
764 # SCM CACHE INSTANCE
751 #==========================================================================
765 #==========================================================================
752
766
753 @property
767 @property
754 def invalidate(self):
768 def invalidate(self):
755 return CacheInvalidation.invalidate(self.repo_name)
769 return CacheInvalidation.invalidate(self.repo_name)
756
770
757 def set_invalidate(self):
771 def set_invalidate(self):
758 """
772 """
759 set a cache for invalidation for this instance
773 set a cache for invalidation for this instance
760 """
774 """
761 CacheInvalidation.set_invalidate(self.repo_name)
775 CacheInvalidation.set_invalidate(self.repo_name)
762
776
763 @LazyProperty
777 @LazyProperty
764 def scm_instance(self):
778 def scm_instance(self):
765 return self.__get_instance()
779 return self.__get_instance()
766
780
767 def scm_instance_cached(self, cache_map=None):
781 def scm_instance_cached(self, cache_map=None):
768 @cache_region('long_term')
782 @cache_region('long_term')
769 def _c(repo_name):
783 def _c(repo_name):
770 return self.__get_instance()
784 return self.__get_instance()
771 rn = self.repo_name
785 rn = self.repo_name
772 log.debug('Getting cached instance of repo')
786 log.debug('Getting cached instance of repo')
773
787
774 if cache_map:
788 if cache_map:
775 # get using prefilled cache_map
789 # get using prefilled cache_map
776 invalidate_repo = cache_map[self.repo_name]
790 invalidate_repo = cache_map[self.repo_name]
777 if invalidate_repo:
791 if invalidate_repo:
778 invalidate_repo = (None if invalidate_repo.cache_active
792 invalidate_repo = (None if invalidate_repo.cache_active
779 else invalidate_repo)
793 else invalidate_repo)
780 else:
794 else:
781 # get from invalidate
795 # get from invalidate
782 invalidate_repo = self.invalidate
796 invalidate_repo = self.invalidate
783
797
784 if invalidate_repo is not None:
798 if invalidate_repo is not None:
785 region_invalidate(_c, None, rn)
799 region_invalidate(_c, None, rn)
786 # update our cache
800 # update our cache
787 CacheInvalidation.set_valid(invalidate_repo.cache_key)
801 CacheInvalidation.set_valid(invalidate_repo.cache_key)
788 return _c(rn)
802 return _c(rn)
789
803
790 def __get_instance(self):
804 def __get_instance(self):
791 repo_full_path = self.repo_full_path
805 repo_full_path = self.repo_full_path
792 try:
806 try:
793 alias = get_scm(repo_full_path)[0]
807 alias = get_scm(repo_full_path)[0]
794 log.debug('Creating instance of %s repository' % alias)
808 log.debug('Creating instance of %s repository' % alias)
795 backend = get_backend(alias)
809 backend = get_backend(alias)
796 except VCSError:
810 except VCSError:
797 log.error(traceback.format_exc())
811 log.error(traceback.format_exc())
798 log.error('Perhaps this repository is in db and not in '
812 log.error('Perhaps this repository is in db and not in '
799 'filesystem run rescan repositories with '
813 'filesystem run rescan repositories with '
800 '"destroy old data " option from admin panel')
814 '"destroy old data " option from admin panel')
801 return
815 return
802
816
803 if alias == 'hg':
817 if alias == 'hg':
804
818
805 repo = backend(safe_str(repo_full_path), create=False,
819 repo = backend(safe_str(repo_full_path), create=False,
806 baseui=self._ui)
820 baseui=self._ui)
807 # skip hidden web repository
821 # skip hidden web repository
808 if repo._get_hidden():
822 if repo._get_hidden():
809 return
823 return
810 else:
824 else:
811 repo = backend(repo_full_path, create=False)
825 repo = backend(repo_full_path, create=False)
812
826
813 return repo
827 return repo
814
828
815
829
816 class RepoGroup(Base, BaseModel):
830 class RepoGroup(Base, BaseModel):
817 __tablename__ = 'groups'
831 __tablename__ = 'groups'
818 __table_args__ = (
832 __table_args__ = (
819 UniqueConstraint('group_name', 'group_parent_id'),
833 UniqueConstraint('group_name', 'group_parent_id'),
820 CheckConstraint('group_id != group_parent_id'),
834 CheckConstraint('group_id != group_parent_id'),
821 {'extend_existing': True, 'mysql_engine': 'InnoDB',
835 {'extend_existing': True, 'mysql_engine': 'InnoDB',
822 'mysql_charset': 'utf8'},
836 'mysql_charset': 'utf8'},
823 )
837 )
824 __mapper_args__ = {'order_by': 'group_name'}
838 __mapper_args__ = {'order_by': 'group_name'}
825
839
826 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
840 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
827 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
841 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
828 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
842 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
829 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
843 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
830
844
831 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
845 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
832 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
846 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
833
847
834 parent_group = relationship('RepoGroup', remote_side=group_id)
848 parent_group = relationship('RepoGroup', remote_side=group_id)
835
849
836 def __init__(self, group_name='', parent_group=None):
850 def __init__(self, group_name='', parent_group=None):
837 self.group_name = group_name
851 self.group_name = group_name
838 self.parent_group = parent_group
852 self.parent_group = parent_group
839
853
840 def __unicode__(self):
854 def __unicode__(self):
841 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
855 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
842 self.group_name)
856 self.group_name)
843
857
844 @classmethod
858 @classmethod
845 def groups_choices(cls):
859 def groups_choices(cls):
846 from webhelpers.html import literal as _literal
860 from webhelpers.html import literal as _literal
847 repo_groups = [('', '')]
861 repo_groups = [('', '')]
848 sep = ' &raquo; '
862 sep = ' &raquo; '
849 _name = lambda k: _literal(sep.join(k))
863 _name = lambda k: _literal(sep.join(k))
850
864
851 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
865 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
852 for x in cls.query().all()])
866 for x in cls.query().all()])
853
867
854 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
868 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
855 return repo_groups
869 return repo_groups
856
870
857 @classmethod
871 @classmethod
858 def url_sep(cls):
872 def url_sep(cls):
859 return URL_SEP
873 return URL_SEP
860
874
861 @classmethod
875 @classmethod
862 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
876 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
863 if case_insensitive:
877 if case_insensitive:
864 gr = cls.query()\
878 gr = cls.query()\
865 .filter(cls.group_name.ilike(group_name))
879 .filter(cls.group_name.ilike(group_name))
866 else:
880 else:
867 gr = cls.query()\
881 gr = cls.query()\
868 .filter(cls.group_name == group_name)
882 .filter(cls.group_name == group_name)
869 if cache:
883 if cache:
870 gr = gr.options(FromCache(
884 gr = gr.options(FromCache(
871 "sql_cache_short",
885 "sql_cache_short",
872 "get_group_%s" % _hash_key(group_name)
886 "get_group_%s" % _hash_key(group_name)
873 )
887 )
874 )
888 )
875 return gr.scalar()
889 return gr.scalar()
876
890
877 @property
891 @property
878 def parents(self):
892 def parents(self):
879 parents_recursion_limit = 5
893 parents_recursion_limit = 5
880 groups = []
894 groups = []
881 if self.parent_group is None:
895 if self.parent_group is None:
882 return groups
896 return groups
883 cur_gr = self.parent_group
897 cur_gr = self.parent_group
884 groups.insert(0, cur_gr)
898 groups.insert(0, cur_gr)
885 cnt = 0
899 cnt = 0
886 while 1:
900 while 1:
887 cnt += 1
901 cnt += 1
888 gr = getattr(cur_gr, 'parent_group', None)
902 gr = getattr(cur_gr, 'parent_group', None)
889 cur_gr = cur_gr.parent_group
903 cur_gr = cur_gr.parent_group
890 if gr is None:
904 if gr is None:
891 break
905 break
892 if cnt == parents_recursion_limit:
906 if cnt == parents_recursion_limit:
893 # this will prevent accidental infinit loops
907 # this will prevent accidental infinit loops
894 log.error('group nested more than %s' %
908 log.error('group nested more than %s' %
895 parents_recursion_limit)
909 parents_recursion_limit)
896 break
910 break
897
911
898 groups.insert(0, gr)
912 groups.insert(0, gr)
899 return groups
913 return groups
900
914
901 @property
915 @property
902 def children(self):
916 def children(self):
903 return RepoGroup.query().filter(RepoGroup.parent_group == self)
917 return RepoGroup.query().filter(RepoGroup.parent_group == self)
904
918
905 @property
919 @property
906 def name(self):
920 def name(self):
907 return self.group_name.split(RepoGroup.url_sep())[-1]
921 return self.group_name.split(RepoGroup.url_sep())[-1]
908
922
909 @property
923 @property
910 def full_path(self):
924 def full_path(self):
911 return self.group_name
925 return self.group_name
912
926
913 @property
927 @property
914 def full_path_splitted(self):
928 def full_path_splitted(self):
915 return self.group_name.split(RepoGroup.url_sep())
929 return self.group_name.split(RepoGroup.url_sep())
916
930
917 @property
931 @property
918 def repositories(self):
932 def repositories(self):
919 return Repository.query()\
933 return Repository.query()\
920 .filter(Repository.group == self)\
934 .filter(Repository.group == self)\
921 .order_by(Repository.repo_name)
935 .order_by(Repository.repo_name)
922
936
923 @property
937 @property
924 def repositories_recursive_count(self):
938 def repositories_recursive_count(self):
925 cnt = self.repositories.count()
939 cnt = self.repositories.count()
926
940
927 def children_count(group):
941 def children_count(group):
928 cnt = 0
942 cnt = 0
929 for child in group.children:
943 for child in group.children:
930 cnt += child.repositories.count()
944 cnt += child.repositories.count()
931 cnt += children_count(child)
945 cnt += children_count(child)
932 return cnt
946 return cnt
933
947
934 return cnt + children_count(self)
948 return cnt + children_count(self)
935
949
936 def get_new_name(self, group_name):
950 def get_new_name(self, group_name):
937 """
951 """
938 returns new full group name based on parent and new name
952 returns new full group name based on parent and new name
939
953
940 :param group_name:
954 :param group_name:
941 """
955 """
942 path_prefix = (self.parent_group.full_path_splitted if
956 path_prefix = (self.parent_group.full_path_splitted if
943 self.parent_group else [])
957 self.parent_group else [])
944 return RepoGroup.url_sep().join(path_prefix + [group_name])
958 return RepoGroup.url_sep().join(path_prefix + [group_name])
945
959
946
960
947 class Permission(Base, BaseModel):
961 class Permission(Base, BaseModel):
948 __tablename__ = 'permissions'
962 __tablename__ = 'permissions'
949 __table_args__ = (
963 __table_args__ = (
950 {'extend_existing': True, 'mysql_engine': 'InnoDB',
964 {'extend_existing': True, 'mysql_engine': 'InnoDB',
951 'mysql_charset': 'utf8'},
965 'mysql_charset': 'utf8'},
952 )
966 )
953 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
967 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
954 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
968 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
955 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
969 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
956
970
957 def __unicode__(self):
971 def __unicode__(self):
958 return u"<%s('%s:%s')>" % (
972 return u"<%s('%s:%s')>" % (
959 self.__class__.__name__, self.permission_id, self.permission_name
973 self.__class__.__name__, self.permission_id, self.permission_name
960 )
974 )
961
975
962 @classmethod
976 @classmethod
963 def get_by_key(cls, key):
977 def get_by_key(cls, key):
964 return cls.query().filter(cls.permission_name == key).scalar()
978 return cls.query().filter(cls.permission_name == key).scalar()
965
979
966 @classmethod
980 @classmethod
967 def get_default_perms(cls, default_user_id):
981 def get_default_perms(cls, default_user_id):
968 q = Session.query(UserRepoToPerm, Repository, cls)\
982 q = Session.query(UserRepoToPerm, Repository, cls)\
969 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
983 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
970 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
984 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
971 .filter(UserRepoToPerm.user_id == default_user_id)
985 .filter(UserRepoToPerm.user_id == default_user_id)
972
986
973 return q.all()
987 return q.all()
974
988
975 @classmethod
989 @classmethod
976 def get_default_group_perms(cls, default_user_id):
990 def get_default_group_perms(cls, default_user_id):
977 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
991 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
978 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
992 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
979 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
993 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
980 .filter(UserRepoGroupToPerm.user_id == default_user_id)
994 .filter(UserRepoGroupToPerm.user_id == default_user_id)
981
995
982 return q.all()
996 return q.all()
983
997
984
998
985 class UserRepoToPerm(Base, BaseModel):
999 class UserRepoToPerm(Base, BaseModel):
986 __tablename__ = 'repo_to_perm'
1000 __tablename__ = 'repo_to_perm'
987 __table_args__ = (
1001 __table_args__ = (
988 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1002 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
989 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1003 {'extend_existing': True, 'mysql_engine': 'InnoDB',
990 'mysql_charset': 'utf8'}
1004 'mysql_charset': 'utf8'}
991 )
1005 )
992 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1006 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
993 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1007 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
994 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1008 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
995 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1009 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
996
1010
997 user = relationship('User')
1011 user = relationship('User')
998 repository = relationship('Repository')
1012 repository = relationship('Repository')
999 permission = relationship('Permission')
1013 permission = relationship('Permission')
1000
1014
1001 @classmethod
1015 @classmethod
1002 def create(cls, user, repository, permission):
1016 def create(cls, user, repository, permission):
1003 n = cls()
1017 n = cls()
1004 n.user = user
1018 n.user = user
1005 n.repository = repository
1019 n.repository = repository
1006 n.permission = permission
1020 n.permission = permission
1007 Session.add(n)
1021 Session.add(n)
1008 return n
1022 return n
1009
1023
1010 def __unicode__(self):
1024 def __unicode__(self):
1011 return u'<user:%s => %s >' % (self.user, self.repository)
1025 return u'<user:%s => %s >' % (self.user, self.repository)
1012
1026
1013
1027
1014 class UserToPerm(Base, BaseModel):
1028 class UserToPerm(Base, BaseModel):
1015 __tablename__ = 'user_to_perm'
1029 __tablename__ = 'user_to_perm'
1016 __table_args__ = (
1030 __table_args__ = (
1017 UniqueConstraint('user_id', 'permission_id'),
1031 UniqueConstraint('user_id', 'permission_id'),
1018 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1032 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1019 'mysql_charset': 'utf8'}
1033 'mysql_charset': 'utf8'}
1020 )
1034 )
1021 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1035 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1022 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1036 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1023 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1037 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1024
1038
1025 user = relationship('User')
1039 user = relationship('User')
1026 permission = relationship('Permission', lazy='joined')
1040 permission = relationship('Permission', lazy='joined')
1027
1041
1028
1042
1029 class UsersGroupRepoToPerm(Base, BaseModel):
1043 class UsersGroupRepoToPerm(Base, BaseModel):
1030 __tablename__ = 'users_group_repo_to_perm'
1044 __tablename__ = 'users_group_repo_to_perm'
1031 __table_args__ = (
1045 __table_args__ = (
1032 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1046 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1033 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1047 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1034 'mysql_charset': 'utf8'}
1048 'mysql_charset': 'utf8'}
1035 )
1049 )
1036 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1050 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1037 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1051 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1038 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1052 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1039 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1053 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1040
1054
1041 users_group = relationship('UsersGroup')
1055 users_group = relationship('UsersGroup')
1042 permission = relationship('Permission')
1056 permission = relationship('Permission')
1043 repository = relationship('Repository')
1057 repository = relationship('Repository')
1044
1058
1045 @classmethod
1059 @classmethod
1046 def create(cls, users_group, repository, permission):
1060 def create(cls, users_group, repository, permission):
1047 n = cls()
1061 n = cls()
1048 n.users_group = users_group
1062 n.users_group = users_group
1049 n.repository = repository
1063 n.repository = repository
1050 n.permission = permission
1064 n.permission = permission
1051 Session.add(n)
1065 Session.add(n)
1052 return n
1066 return n
1053
1067
1054 def __unicode__(self):
1068 def __unicode__(self):
1055 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1069 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1056
1070
1057
1071
1058 class UsersGroupToPerm(Base, BaseModel):
1072 class UsersGroupToPerm(Base, BaseModel):
1059 __tablename__ = 'users_group_to_perm'
1073 __tablename__ = 'users_group_to_perm'
1060 __table_args__ = (
1074 __table_args__ = (
1061 UniqueConstraint('users_group_id', 'permission_id',),
1075 UniqueConstraint('users_group_id', 'permission_id',),
1062 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1076 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1063 'mysql_charset': 'utf8'}
1077 'mysql_charset': 'utf8'}
1064 )
1078 )
1065 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1079 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1066 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1080 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1067 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1081 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1068
1082
1069 users_group = relationship('UsersGroup')
1083 users_group = relationship('UsersGroup')
1070 permission = relationship('Permission')
1084 permission = relationship('Permission')
1071
1085
1072
1086
1073 class UserRepoGroupToPerm(Base, BaseModel):
1087 class UserRepoGroupToPerm(Base, BaseModel):
1074 __tablename__ = 'user_repo_group_to_perm'
1088 __tablename__ = 'user_repo_group_to_perm'
1075 __table_args__ = (
1089 __table_args__ = (
1076 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1090 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1077 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1091 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1078 'mysql_charset': 'utf8'}
1092 'mysql_charset': 'utf8'}
1079 )
1093 )
1080
1094
1081 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1095 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1082 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1096 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1083 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1097 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1084 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1098 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1085
1099
1086 user = relationship('User')
1100 user = relationship('User')
1087 group = relationship('RepoGroup')
1101 group = relationship('RepoGroup')
1088 permission = relationship('Permission')
1102 permission = relationship('Permission')
1089
1103
1090
1104
1091 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1105 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1092 __tablename__ = 'users_group_repo_group_to_perm'
1106 __tablename__ = 'users_group_repo_group_to_perm'
1093 __table_args__ = (
1107 __table_args__ = (
1094 UniqueConstraint('users_group_id', 'group_id'),
1108 UniqueConstraint('users_group_id', 'group_id'),
1095 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1109 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1096 'mysql_charset': 'utf8'}
1110 'mysql_charset': 'utf8'}
1097 )
1111 )
1098
1112
1099 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1113 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1100 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1114 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1101 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1115 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1102 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1116 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1103
1117
1104 users_group = relationship('UsersGroup')
1118 users_group = relationship('UsersGroup')
1105 permission = relationship('Permission')
1119 permission = relationship('Permission')
1106 group = relationship('RepoGroup')
1120 group = relationship('RepoGroup')
1107
1121
1108
1122
1109 class Statistics(Base, BaseModel):
1123 class Statistics(Base, BaseModel):
1110 __tablename__ = 'statistics'
1124 __tablename__ = 'statistics'
1111 __table_args__ = (
1125 __table_args__ = (
1112 UniqueConstraint('repository_id'),
1126 UniqueConstraint('repository_id'),
1113 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1127 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1114 'mysql_charset': 'utf8'}
1128 'mysql_charset': 'utf8'}
1115 )
1129 )
1116 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1130 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1117 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1131 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1118 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1132 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1119 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1133 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1120 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1134 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1121 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1135 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1122
1136
1123 repository = relationship('Repository', single_parent=True)
1137 repository = relationship('Repository', single_parent=True)
1124
1138
1125
1139
1126 class UserFollowing(Base, BaseModel):
1140 class UserFollowing(Base, BaseModel):
1127 __tablename__ = 'user_followings'
1141 __tablename__ = 'user_followings'
1128 __table_args__ = (
1142 __table_args__ = (
1129 UniqueConstraint('user_id', 'follows_repository_id'),
1143 UniqueConstraint('user_id', 'follows_repository_id'),
1130 UniqueConstraint('user_id', 'follows_user_id'),
1144 UniqueConstraint('user_id', 'follows_user_id'),
1131 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1145 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1132 'mysql_charset': 'utf8'}
1146 'mysql_charset': 'utf8'}
1133 )
1147 )
1134
1148
1135 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1149 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1136 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1150 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1137 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1151 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1138 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1152 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1139 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1153 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1140
1154
1141 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1155 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1142
1156
1143 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1157 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1144 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1158 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1145
1159
1146 @classmethod
1160 @classmethod
1147 def get_repo_followers(cls, repo_id):
1161 def get_repo_followers(cls, repo_id):
1148 return cls.query().filter(cls.follows_repo_id == repo_id)
1162 return cls.query().filter(cls.follows_repo_id == repo_id)
1149
1163
1150
1164
1151 class CacheInvalidation(Base, BaseModel):
1165 class CacheInvalidation(Base, BaseModel):
1152 __tablename__ = 'cache_invalidation'
1166 __tablename__ = 'cache_invalidation'
1153 __table_args__ = (
1167 __table_args__ = (
1154 UniqueConstraint('cache_key'),
1168 UniqueConstraint('cache_key'),
1155 Index('key_idx', 'cache_key'),
1169 Index('key_idx', 'cache_key'),
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1170 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1157 'mysql_charset': 'utf8'},
1171 'mysql_charset': 'utf8'},
1158 )
1172 )
1159 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1173 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1160 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1174 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1161 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1175 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1162 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1176 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1163
1177
1164 def __init__(self, cache_key, cache_args=''):
1178 def __init__(self, cache_key, cache_args=''):
1165 self.cache_key = cache_key
1179 self.cache_key = cache_key
1166 self.cache_args = cache_args
1180 self.cache_args = cache_args
1167 self.cache_active = False
1181 self.cache_active = False
1168
1182
1169 def __unicode__(self):
1183 def __unicode__(self):
1170 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1184 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1171 self.cache_id, self.cache_key)
1185 self.cache_id, self.cache_key)
1172
1186
1173 @classmethod
1187 @classmethod
1174 def clear_cache(cls):
1188 def clear_cache(cls):
1175 cls.query().delete()
1189 cls.query().delete()
1176
1190
1177 @classmethod
1191 @classmethod
1178 def _get_key(cls, key):
1192 def _get_key(cls, key):
1179 """
1193 """
1180 Wrapper for generating a key, together with a prefix
1194 Wrapper for generating a key, together with a prefix
1181
1195
1182 :param key:
1196 :param key:
1183 """
1197 """
1184 import rhodecode
1198 import rhodecode
1185 prefix = ''
1199 prefix = ''
1186 iid = rhodecode.CONFIG.get('instance_id')
1200 iid = rhodecode.CONFIG.get('instance_id')
1187 if iid:
1201 if iid:
1188 prefix = iid
1202 prefix = iid
1189 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1203 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1190
1204
1191 @classmethod
1205 @classmethod
1192 def get_by_key(cls, key):
1206 def get_by_key(cls, key):
1193 return cls.query().filter(cls.cache_key == key).scalar()
1207 return cls.query().filter(cls.cache_key == key).scalar()
1194
1208
1195 @classmethod
1209 @classmethod
1196 def _get_or_create_key(cls, key, prefix, org_key):
1210 def _get_or_create_key(cls, key, prefix, org_key):
1197 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1211 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1198 if not inv_obj:
1212 if not inv_obj:
1199 try:
1213 try:
1200 inv_obj = CacheInvalidation(key, org_key)
1214 inv_obj = CacheInvalidation(key, org_key)
1201 Session.add(inv_obj)
1215 Session.add(inv_obj)
1202 Session.commit()
1216 Session.commit()
1203 except Exception:
1217 except Exception:
1204 log.error(traceback.format_exc())
1218 log.error(traceback.format_exc())
1205 Session.rollback()
1219 Session.rollback()
1206 return inv_obj
1220 return inv_obj
1207
1221
1208 @classmethod
1222 @classmethod
1209 def invalidate(cls, key):
1223 def invalidate(cls, key):
1210 """
1224 """
1211 Returns Invalidation object if this given key should be invalidated
1225 Returns Invalidation object if this given key should be invalidated
1212 None otherwise. `cache_active = False` means that this cache
1226 None otherwise. `cache_active = False` means that this cache
1213 state is not valid and needs to be invalidated
1227 state is not valid and needs to be invalidated
1214
1228
1215 :param key:
1229 :param key:
1216 """
1230 """
1217
1231
1218 key, _prefix, _org_key = cls._get_key(key)
1232 key, _prefix, _org_key = cls._get_key(key)
1219 inv = cls._get_or_create_key(key, _prefix, _org_key)
1233 inv = cls._get_or_create_key(key, _prefix, _org_key)
1220
1234
1221 if inv and inv.cache_active is False:
1235 if inv and inv.cache_active is False:
1222 return inv
1236 return inv
1223
1237
1224 @classmethod
1238 @classmethod
1225 def set_invalidate(cls, key):
1239 def set_invalidate(cls, key):
1226 """
1240 """
1227 Mark this Cache key for invalidation
1241 Mark this Cache key for invalidation
1228
1242
1229 :param key:
1243 :param key:
1230 """
1244 """
1231
1245
1232 key, _prefix, _org_key = cls._get_key(key)
1246 key, _prefix, _org_key = cls._get_key(key)
1233 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1247 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1234 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1248 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1235 _org_key))
1249 _org_key))
1236 try:
1250 try:
1237 for inv_obj in inv_objs:
1251 for inv_obj in inv_objs:
1238 if inv_obj:
1252 if inv_obj:
1239 inv_obj.cache_active = False
1253 inv_obj.cache_active = False
1240
1254
1241 Session.add(inv_obj)
1255 Session.add(inv_obj)
1242 Session.commit()
1256 Session.commit()
1243 except Exception:
1257 except Exception:
1244 log.error(traceback.format_exc())
1258 log.error(traceback.format_exc())
1245 Session.rollback()
1259 Session.rollback()
1246
1260
1247 @classmethod
1261 @classmethod
1248 def set_valid(cls, key):
1262 def set_valid(cls, key):
1249 """
1263 """
1250 Mark this cache key as active and currently cached
1264 Mark this cache key as active and currently cached
1251
1265
1252 :param key:
1266 :param key:
1253 """
1267 """
1254 inv_obj = cls.get_by_key(key)
1268 inv_obj = cls.get_by_key(key)
1255 inv_obj.cache_active = True
1269 inv_obj.cache_active = True
1256 Session.add(inv_obj)
1270 Session.add(inv_obj)
1257 Session.commit()
1271 Session.commit()
1258
1272
1259 @classmethod
1273 @classmethod
1260 def get_cache_map(cls):
1274 def get_cache_map(cls):
1261
1275
1262 class cachemapdict(dict):
1276 class cachemapdict(dict):
1263
1277
1264 def __init__(self, *args, **kwargs):
1278 def __init__(self, *args, **kwargs):
1265 fixkey = kwargs.get('fixkey')
1279 fixkey = kwargs.get('fixkey')
1266 if fixkey:
1280 if fixkey:
1267 del kwargs['fixkey']
1281 del kwargs['fixkey']
1268 self.fixkey = fixkey
1282 self.fixkey = fixkey
1269 super(cachemapdict, self).__init__(*args, **kwargs)
1283 super(cachemapdict, self).__init__(*args, **kwargs)
1270
1284
1271 def __getattr__(self, name):
1285 def __getattr__(self, name):
1272 key = name
1286 key = name
1273 if self.fixkey:
1287 if self.fixkey:
1274 key, _prefix, _org_key = cls._get_key(key)
1288 key, _prefix, _org_key = cls._get_key(key)
1275 if key in self.__dict__:
1289 if key in self.__dict__:
1276 return self.__dict__[key]
1290 return self.__dict__[key]
1277 else:
1291 else:
1278 return self[key]
1292 return self[key]
1279
1293
1280 def __getitem__(self, key):
1294 def __getitem__(self, key):
1281 if self.fixkey:
1295 if self.fixkey:
1282 key, _prefix, _org_key = cls._get_key(key)
1296 key, _prefix, _org_key = cls._get_key(key)
1283 try:
1297 try:
1284 return super(cachemapdict, self).__getitem__(key)
1298 return super(cachemapdict, self).__getitem__(key)
1285 except KeyError:
1299 except KeyError:
1286 return
1300 return
1287
1301
1288 cache_map = cachemapdict(fixkey=True)
1302 cache_map = cachemapdict(fixkey=True)
1289 for obj in cls.query().all():
1303 for obj in cls.query().all():
1290 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1304 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1291 return cache_map
1305 return cache_map
1292
1306
1293
1307
1294 class ChangesetComment(Base, BaseModel):
1308 class ChangesetComment(Base, BaseModel):
1295 __tablename__ = 'changeset_comments'
1309 __tablename__ = 'changeset_comments'
1296 __table_args__ = (
1310 __table_args__ = (
1297 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1311 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1298 'mysql_charset': 'utf8'},
1312 'mysql_charset': 'utf8'},
1299 )
1313 )
1300 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1314 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1301 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1315 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1302 revision = Column('revision', String(40), nullable=False)
1316 revision = Column('revision', String(40), nullable=False)
1303 line_no = Column('line_no', Unicode(10), nullable=True)
1317 line_no = Column('line_no', Unicode(10), nullable=True)
1304 f_path = Column('f_path', Unicode(1000), nullable=True)
1318 f_path = Column('f_path', Unicode(1000), nullable=True)
1305 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1319 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1306 text = Column('text', Unicode(25000), nullable=False)
1320 text = Column('text', Unicode(25000), nullable=False)
1307 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1321 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1308
1322
1309 author = relationship('User', lazy='joined')
1323 author = relationship('User', lazy='joined')
1310 repo = relationship('Repository')
1324 repo = relationship('Repository')
1311 status_change = relationship('ChangesetStatus', uselist=False)
1325 status_change = relationship('ChangesetStatus', uselist=False)
1312
1326
1313 @classmethod
1327 @classmethod
1314 def get_users(cls, revision):
1328 def get_users(cls, revision):
1315 """
1329 """
1316 Returns user associated with this changesetComment. ie those
1330 Returns user associated with this changesetComment. ie those
1317 who actually commented
1331 who actually commented
1318
1332
1319 :param cls:
1333 :param cls:
1320 :param revision:
1334 :param revision:
1321 """
1335 """
1322 return Session.query(User)\
1336 return Session.query(User)\
1323 .filter(cls.revision == revision)\
1337 .filter(cls.revision == revision)\
1324 .join(ChangesetComment.author).all()
1338 .join(ChangesetComment.author).all()
1325
1339
1326
1340
1327 class ChangesetStatus(Base, BaseModel):
1341 class ChangesetStatus(Base, BaseModel):
1328 __tablename__ = 'changeset_statuses'
1342 __tablename__ = 'changeset_statuses'
1329 __table_args__ = (
1343 __table_args__ = (
1330 UniqueConstraint('repo_id', 'revision', 'version'),
1344 UniqueConstraint('repo_id', 'revision', 'version'),
1331 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1345 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1332 'mysql_charset': 'utf8'}
1346 'mysql_charset': 'utf8'}
1333 )
1347 )
1334
1348
1335 STATUSES = [
1349 STATUSES = [
1336 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1350 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1337 ('approved', _("Approved")),
1351 ('approved', _("Approved")),
1338 ('rejected', _("Rejected")),
1352 ('rejected', _("Rejected")),
1339 ('under_review', _("Under Review")),
1353 ('under_review', _("Under Review")),
1340 ]
1354 ]
1341 DEFAULT = STATUSES[0][0]
1355 DEFAULT = STATUSES[0][0]
1342
1356
1343 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1357 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1344 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1358 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1345 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1346 revision = Column('revision', String(40), nullable=False)
1360 revision = Column('revision', String(40), nullable=False)
1347 status = Column('status', String(128), nullable=False, default=DEFAULT)
1361 status = Column('status', String(128), nullable=False, default=DEFAULT)
1348 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1362 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1349 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1363 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1350 version = Column('version', Integer(), nullable=False, default=0)
1364 version = Column('version', Integer(), nullable=False, default=0)
1351 author = relationship('User', lazy='joined')
1365 author = relationship('User', lazy='joined')
1352 repo = relationship('Repository')
1366 repo = relationship('Repository')
1353 comment = relationship('ChangesetComment', lazy='joined')
1367 comment = relationship('ChangesetComment', lazy='joined')
1354
1368
1355 @classmethod
1369 @classmethod
1356 def get_status_lbl(cls, value):
1370 def get_status_lbl(cls, value):
1357 return dict(cls.STATUSES).get(value)
1371 return dict(cls.STATUSES).get(value)
1358
1372
1359 @property
1373 @property
1360 def status_lbl(self):
1374 def status_lbl(self):
1361 return ChangesetStatus.get_status_lbl(self.status)
1375 return ChangesetStatus.get_status_lbl(self.status)
1362
1376
1363
1377
1364 class Notification(Base, BaseModel):
1378 class Notification(Base, BaseModel):
1365 __tablename__ = 'notifications'
1379 __tablename__ = 'notifications'
1366 __table_args__ = (
1380 __table_args__ = (
1367 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1381 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1368 'mysql_charset': 'utf8'},
1382 'mysql_charset': 'utf8'},
1369 )
1383 )
1370
1384
1371 TYPE_CHANGESET_COMMENT = u'cs_comment'
1385 TYPE_CHANGESET_COMMENT = u'cs_comment'
1372 TYPE_MESSAGE = u'message'
1386 TYPE_MESSAGE = u'message'
1373 TYPE_MENTION = u'mention'
1387 TYPE_MENTION = u'mention'
1374 TYPE_REGISTRATION = u'registration'
1388 TYPE_REGISTRATION = u'registration'
1375 TYPE_PULL_REQUEST = u'pull_request'
1389 TYPE_PULL_REQUEST = u'pull_request'
1376
1390
1377 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1391 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1378 subject = Column('subject', Unicode(512), nullable=True)
1392 subject = Column('subject', Unicode(512), nullable=True)
1379 body = Column('body', Unicode(50000), nullable=True)
1393 body = Column('body', Unicode(50000), nullable=True)
1380 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1394 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1381 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1395 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1382 type_ = Column('type', Unicode(256))
1396 type_ = Column('type', Unicode(256))
1383
1397
1384 created_by_user = relationship('User')
1398 created_by_user = relationship('User')
1385 notifications_to_users = relationship('UserNotification', lazy='joined',
1399 notifications_to_users = relationship('UserNotification', lazy='joined',
1386 cascade="all, delete, delete-orphan")
1400 cascade="all, delete, delete-orphan")
1387
1401
1388 @property
1402 @property
1389 def recipients(self):
1403 def recipients(self):
1390 return [x.user for x in UserNotification.query()\
1404 return [x.user for x in UserNotification.query()\
1391 .filter(UserNotification.notification == self)\
1405 .filter(UserNotification.notification == self)\
1392 .order_by(UserNotification.user).all()]
1406 .order_by(UserNotification.user).all()]
1393
1407
1394 @classmethod
1408 @classmethod
1395 def create(cls, created_by, subject, body, recipients, type_=None):
1409 def create(cls, created_by, subject, body, recipients, type_=None):
1396 if type_ is None:
1410 if type_ is None:
1397 type_ = Notification.TYPE_MESSAGE
1411 type_ = Notification.TYPE_MESSAGE
1398
1412
1399 notification = cls()
1413 notification = cls()
1400 notification.created_by_user = created_by
1414 notification.created_by_user = created_by
1401 notification.subject = subject
1415 notification.subject = subject
1402 notification.body = body
1416 notification.body = body
1403 notification.type_ = type_
1417 notification.type_ = type_
1404 notification.created_on = datetime.datetime.now()
1418 notification.created_on = datetime.datetime.now()
1405
1419
1406 for u in recipients:
1420 for u in recipients:
1407 assoc = UserNotification()
1421 assoc = UserNotification()
1408 assoc.notification = notification
1422 assoc.notification = notification
1409 u.notifications.append(assoc)
1423 u.notifications.append(assoc)
1410 Session.add(notification)
1424 Session.add(notification)
1411 return notification
1425 return notification
1412
1426
1413 @property
1427 @property
1414 def description(self):
1428 def description(self):
1415 from rhodecode.model.notification import NotificationModel
1429 from rhodecode.model.notification import NotificationModel
1416 return NotificationModel().make_description(self)
1430 return NotificationModel().make_description(self)
1417
1431
1418
1432
1419 class UserNotification(Base, BaseModel):
1433 class UserNotification(Base, BaseModel):
1420 __tablename__ = 'user_to_notification'
1434 __tablename__ = 'user_to_notification'
1421 __table_args__ = (
1435 __table_args__ = (
1422 UniqueConstraint('user_id', 'notification_id'),
1436 UniqueConstraint('user_id', 'notification_id'),
1423 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1437 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1424 'mysql_charset': 'utf8'}
1438 'mysql_charset': 'utf8'}
1425 )
1439 )
1426 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1440 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1427 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1441 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1428 read = Column('read', Boolean, default=False)
1442 read = Column('read', Boolean, default=False)
1429 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1443 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1430
1444
1431 user = relationship('User', lazy="joined")
1445 user = relationship('User', lazy="joined")
1432 notification = relationship('Notification', lazy="joined",
1446 notification = relationship('Notification', lazy="joined",
1433 order_by=lambda: Notification.created_on.desc(),)
1447 order_by=lambda: Notification.created_on.desc(),)
1434
1448
1435 def mark_as_read(self):
1449 def mark_as_read(self):
1436 self.read = True
1450 self.read = True
1437 Session.add(self)
1451 Session.add(self)
1438
1452
1439
1453
1440 class DbMigrateVersion(Base, BaseModel):
1454 class DbMigrateVersion(Base, BaseModel):
1441 __tablename__ = 'db_migrate_version'
1455 __tablename__ = 'db_migrate_version'
1442 __table_args__ = (
1456 __table_args__ = (
1443 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1457 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1444 'mysql_charset': 'utf8'},
1458 'mysql_charset': 'utf8'},
1445 )
1459 )
1446 repository_id = Column('repository_id', String(250), primary_key=True)
1460 repository_id = Column('repository_id', String(250), primary_key=True)
1447 repository_path = Column('repository_path', Text)
1461 repository_path = Column('repository_path', Text)
1448 version = Column('version', Integer)
1462 version = Column('version', Integer)
@@ -1,90 +1,77 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(u'Home',h.url('/'))}
9 ${h.link_to(u'Home',h.url('/'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 &raquo;
12 &raquo;
13 ${_('Compare')}
13 ${_('Compare')}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('changelog')}
17 ${self.menu('changelog')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <div class="table">
26 <div class="table">
27 <div id="body" class="diffblock">
27 <div id="body" class="diffblock">
28 <div class="code-header cv">
28 <div class="code-header cv">
29 <h3 class="code-header-title">${_('Compare View')}</h3>
29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 <div>
30 <div>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
35 <div id="changeset_compare_view_content">
35 <div id="changeset_compare_view_content">
36 <div class="container">
36 ##CS
37 <table class="compare_view_commits noborder">
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Changesets')}</div>
38 %for cnt, cs in enumerate(c.cs_ranges):
38 <%include file="compare_cs.html" />
39 <tr>
39
40 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
40 ## FILES
41 <td>
42 %if cs.raw_id in c.statuses:
43 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
44 %endif
45 </td>
46 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
47 <td><div class="author">${h.person(cs.author)}</div></td>
48 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
49 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
50 </tr>
51 %endfor
52 </table>
53 </div>
54 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
55 <div class="cs_files">
42 <div class="cs_files">
56 %for fid, change, f, stat in c.files:
43 %for fid, change, f, stat in c.files:
57 <div class="cs_${change}">
44 <div class="cs_${change}">
58 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
45 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
59 <div class="changes">${h.fancy_file_stats(stat)}</div>
46 <div class="changes">${h.fancy_file_stats(stat)}</div>
60 </div>
47 </div>
61 %endfor
48 %endfor
62 </div>
49 </div>
63 </div>
50 </div>
64 </div>
51 </div>
65
52
66 ## diff block
53 ## diff block
67 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
54 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
68 %for fid, change, f, stat in c.files:
55 %for fid, change, f, stat in c.files:
69 ${diff_block.diff_block_simple([c.changes[fid]])}
56 ${diff_block.diff_block_simple([c.changes[fid]])}
70 %endfor
57 %endfor
71
58
72 <script type="text/javascript">
59 <script type="text/javascript">
73
60
74 YUE.onDOMReady(function(){
61 YUE.onDOMReady(function(){
75
62
76 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
63 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
77 var act = e.currentTarget.nextElementSibling;
64 var act = e.currentTarget.nextElementSibling;
78
65
79 if(YUD.hasClass(act,'active')){
66 if(YUD.hasClass(act,'active')){
80 YUD.removeClass(act,'active');
67 YUD.removeClass(act,'active');
81 YUD.setStyle(act,'display','none');
68 YUD.setStyle(act,'display','none');
82 }else{
69 }else{
83 YUD.addClass(act,'active');
70 YUD.addClass(act,'active');
84 YUD.setStyle(act,'display','');
71 YUD.setStyle(act,'display','');
85 }
72 }
86 });
73 });
87 })
74 })
88 </script>
75 </script>
89 </div>
76 </div>
90 </%def>
77 </%def>
@@ -1,91 +1,185 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Pull request')}
4 ${c.repo_name} ${_('Pull request')}
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('changelog_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('Pull request')}
12 ${_('Pull request')}
13 </%def>
13 </%def>
14
14
15 <%def name="main()">
15 <%def name="main()">
16
16
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 <div style="padding:30px">
22 ${h.form(url('#'),method='put', id='pull_request_form')}
23 <div style="float:left;padding:30px">
23 ##ORG
24 ##ORG
24 <div style="float:left">
25 <div style="float:left">
25 <div class="fork_user">
26 <div class="fork_user">
26 <div class="gravatar">
27 <div class="gravatar">
27 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
28 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
28 </div>
29 </div>
29 <span style="font-size: 20px">
30 <span style="font-size: 20px">
30 ${h.select('other','',['%s/%s' % (c.rhodecode_db_repo.user.username,c.repo_name)])}:${h.select('other_ref','',c.org_refs)}
31 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
31 </span>
32 </span>
32 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
33 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
33 </div>
34 </div>
34 <div style="clear:both;padding-top: 10px"></div>
35 <div style="clear:both;padding-top: 10px"></div>
35 </div>
36 </div>
36 <div style="float:left;font-size:24px;padding:0px 20px">
37 <div style="float:left;font-size:24px;padding:0px 20px">
37 <img src="${h.url('/images/arrow_right_64.png')}"/>
38 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
38 </div>
39 </div>
39
40
40 ##OTHER, most Probably the PARENT OF THIS FORK
41 ##OTHER, most Probably the PARENT OF THIS FORK
41 <div style="float:left">
42 <div style="float:left">
42 <div class="fork_user">
43 <div class="fork_user">
43 <div class="gravatar">
44 <div class="gravatar">
44 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
45 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
45 </div>
46 </div>
46 <span style="font-size: 20px">
47 <span style="font-size: 20px">
47 ${h.select('orther','',c.sources)}:${h.select('other_ref','',c.org_refs)}
48 ${h.select('other_repo','',c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
48 </span>
49 </span>
49 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
50 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
50 </div>
51 </div>
51 <div style="clear:both;padding-top: 10px"></div>
52 <div style="clear:both;padding-top: 10px"></div>
52 </div>
53 </div>
54 <div style="float:left;padding:5px 5px 5px 15px">
55 <span>
56 <a id="refresh" href="#">
57 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
58 ${_('refresh overview')}
59 </a>
60 </span>
53 </div>
61 </div>
62 <div style="clear:both;padding-top: 10px"></div>
63 <div style="float:left" id="pull_request_overview">
64 </div>
65 </div>
66 <div style="float:left; border-left:1px dashed #eee">
67 <h4>${_('Pull request reviewers')}</h4>
68 <div id="reviewers" style="padding:0px 0px 0px 15px">
69 ##TODO: make this nicer :)
70 <table class="table noborder">
71 <tr>
72 <td>
73 <div>
74 <div style="float:left">
75 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen reviewers')}</div>
76 ${h.select('review_members',[x[0] for x in c.review_members],c.review_members,multiple=True,size=8,style="min-width:210px")}
77 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
78 ${_('Remove all elements')}
79 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
80 </div>
81 </div>
82 <div style="float:left;width:20px;padding-top:50px">
83 <img alt="add" id="add_element"
84 style="padding:2px;cursor:pointer"
85 src="${h.url('/images/icons/arrow_left.png')}"/>
86 <br />
87 <img alt="remove" id="remove_element"
88 style="padding:2px;cursor:pointer"
89 src="${h.url('/images/icons/arrow_right.png')}"/>
90 </div>
91 <div style="float:left">
92 <div class="text" style="padding: 0px 0px 6px;">${_('Available reviewers')}</div>
93 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
94 <div id="add_all_elements" style="cursor:pointer;text-align:center">
95 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
96 ${_('Add all elements')}
97 </div>
98 </div>
99 </div>
100 </td>
101 </tr>
102 </table>
103 </div>
104 </div>
105 <h3>${_('Create new pull request')}</h3>
54
106
55 <h3>${_('New pull request')} from USER:REF into PARENT:REF</h3>
56 ${h.form(url('#'),method='put')}
57 <div class="form">
107 <div class="form">
58 <!-- fields -->
108 <!-- fields -->
59
109
60 <div class="fields">
110 <div class="fields">
61
111
62 <div class="field">
112 <div class="field">
63 <div class="label">
113 <div class="label">
64 <label for="pullrequest_title">${_('Title')}:</label>
114 <label for="pullrequest_title">${_('Title')}:</label>
65 </div>
115 </div>
66 <div class="input">
116 <div class="input">
67 ${h.text('pullrequest_title',size=30)}
117 ${h.text('pullrequest_title',size=30)}
68 </div>
118 </div>
69 </div>
119 </div>
70
120
71 <div class="field">
121 <div class="field">
72 <div class="label label-textarea">
122 <div class="label label-textarea">
73 <label for="pullrequest_desc">${_('description')}:</label>
123 <label for="pullrequest_desc">${_('description')}:</label>
74 </div>
124 </div>
75 <div class="textarea text-area editor">
125 <div class="textarea text-area editor">
76 ${h.textarea('pullrequest_desc',size=30)}
126 ${h.textarea('pullrequest_desc',size=30)}
77 </div>
127 </div>
78 </div>
128 </div>
79
129
80 <div class="buttons">
130 <div class="buttons">
81 ${h.submit('save',_('Send pull request'),class_="ui-button")}
131 ${h.submit('save',_('Send pull request'),class_="ui-button")}
82 ${h.reset('reset',_('Reset'),class_="ui-button")}
132 ${h.reset('reset',_('Reset'),class_="ui-button")}
83 </div>
133 </div>
84 </div>
134 </div>
85 </div>
135 </div>
86 ${h.end_form()}
136 ${h.end_form()}
87
137
88
89 </div>
138 </div>
90
139
140 <script type="text/javascript">
141 MultiSelectWidget('review_members','available_members','pull_request_form');
142
143 var loadPreview = function(){
144 var url = "${h.url('compare_url',
145 repo_name='org_repo',
146 org_ref_type='branch', org_ref='org_ref',
147 other_ref_type='branch', other_ref='other_ref',
148 repo='other_repo')}";
149
150 var select_refs = YUQ('#pull_request_form select.refs')
151
152 for(var i=0;i<select_refs.length;i++){
153 var select_ref = select_refs[i];
154 var select_ref_data = select_ref.value.split(':');
155 var key = null;
156 var val = null;
157 if(select_ref_data.length>1){
158 key = select_ref.name+"_type";
159 val = select_ref_data[0];
160 url = url.replace(key,val);
161
162 key = select_ref.name;
163 val = select_ref_data[1];
164 url = url.replace(key,val);
165
166 }else{
167 key = select_ref.name;
168 val = select_ref.value;
169 url = url.replace(key,val);
170 }
171 }
172
173 ypjax(url,'pull_request_overview', function(data){})
174 }
175 YUE.on('refresh','click',function(e){
176 loadPreview()
177 })
178
179 //lazy load after 0.5
180
181 setTimeout(loadPreview,500)
182
183 </script>
184
91 </%def>
185 </%def>
General Comments 0
You need to be logged in to leave comments. Login now