##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r4357:77bbe14f merge stable
parent child Browse files
Show More
@@ -0,0 +1,156 b''
1 |RCE| 4.19.0 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2020-05-22
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - Pull requests: add information about changes in source repositories in pull-request show page.
14 Fixes #5611, Fixes #5561
15 Added new preview for size (commits/files) of PRs before opening, this is now based
16 on the special logic that calculates common ancestor and has access to preview diff
17 Store common ancestor in DB so updates of pull-requests are consistent
18 - Pull requests: fixed case for GIT repositories when a merge check failed due to
19 merge conflicts the pull request wrongly reported missing commits.
20 we're now searching for dangling commits in a repo that has them and cannot see them
21 because of failed merge checks.
22 - Pull requests: allow filter displayed results by author
23 - Pull requests: added filters to my account pull requests page.
24 - Quick search: added ability to search for pull-requests using `pr:` prefix.
25 Permissions are checked against the access to target repositories, and users
26 can now search for pull request number, description or title.
27 - UI: replaced js prompts with sweet-alert prompts.
28 - SVN: bumped shipped SVN to 1.13.0 release.
29 - Integration Hooks: added new hooks for comments on pull requests and commits.
30 Allows writing custom actions on top of commenting.
31 E.g `@CI-BOT re-test` could trigger CI job to re-test a pull requests or commit.
32 Added new rcextension hooks, Fixes #5583, and added examples on how to trigger CI build on certain comments.
33 - Exception tracker: added possibility to send notification email when server encountered an unhandled exception.
34 new .ini file flag: `exception_tracker.send_email = false` and `exception_tracker.send_email_recipients =`
35 can be set to enable this function.
36 - Mercurial: enable enhanced diffs for Mercurial that show context of changed functions inside the diff.
37 This makes diff actually more consistent with how GIT backend shows them. Fixes #5614
38
39
40 General
41 ^^^^^^^
42
43 - Pull requests: fixed small UI glitches in pull request view.
44 - System Info: Python packages now expose the package location info.
45 - API: added new flag to comment api to disable email sending when creating a comment.
46 - Exceptions: don't report lookup errors as exceptions stored in the exception store.
47 Those are regular not found problems that don't indicate any exceptional case
48 also make the errors report nicer, not as KeyError, or generic Exception
49 - Exception tracker: store request info if available to track which URL caused an error.
50 - Integrations: handle new commenting events and optimize calls for Jira/Redmine
51 Speed up issue fetching by optimizing how Jira/Redmine client behaves
52 For redmine we don't iterate issues anymore which is *much* faster, and makes pushes with tickets faster.
53 - SVN: allow to specify any svn compatible version string not only hardcoded values.
54 The new SVN code allows to specify this by numeric values now. e.g 1.13 etc.
55 Fixes #5605.
56 - Emails: added `premailer` parsing for inline style formatting to make emails render
57 nicer on all email clients.
58 - Repositories: switched repo type selector to radio buttons and preserve order of
59 enabled backends inside .ini files.
60 - Repositories: show recommendation for updating hooks if they are outdated.
61 - Files: add pre-commit checks on file edit/delete/add operations. This prevents
62 loosing content while editing when repositories changes during those operations.
63 Fixes #5607.
64 - Files: changed the copy path label to indicate we're actually copying only the path.
65 Added copy permalink helper to copy the url quickly. Fixes #5602
66 - LDAP: updated ldap plugin to help with debug and support by extending logging and
67 improving error messages.
68 - LDAP: fixed example LDAPs port.
69 - Dependencies: bump redmine client.
70 - Dependencies: bumped bleach==3.1.3
71 - Dependencies: bumped webtest==2.0.34
72 - Dependencies: bumped packaging==20.3
73 - Dependencies: bumped pyparsing==2.4.7
74 - Dependencies: bumped sqlalchemy==1.3.15
75 - Dependencies: bumped hupper==1.10.2
76 - Dependencies: bumped alembic==1.4.2
77 - Dependencies: bumped wcwidth==0.1.9
78 - Dependencies: bumped python-ldap==3.2.0
79 - Dependencies: bumped importlib-metadata==1.5.0
80 - Dependencies: bumped redis==3.4.1
81 - Dependencies: bumped importlib-metadata==1.6.0
82 - Dependencies: bumped pytz==2019.3
83 - Dependencies: bumped paste==3.4.0
84 - Dependencies: bumped weberror==0.13.1
85 - Dependencies: bumped pyparsing==2.4.6
86 - Dependencies: bumped ipdb==0.13.2
87 - Dependencies: bumped pastedeploy==2.1.0
88 - Dependencies: bumped docutils==0.16.0
89 - Dependencies: bumped pyramid-debugtoolbar==4.6.1
90 - Dependencies: bumped gevent==1.5.0
91 - Dependencies: bumped psutil==5.7.0
92
93
94 Security
95 ^^^^^^^^
96
97 - Logging: expose usernames in the logs for each request made to RhodeCode.
98 This enables auditing capabilities for all actions against the web interface.
99 - Users: increased security on the way we're displaying authentication tokens.
100 We don't expose all on single page. Request needs a validation before viewing of each token.
101 - Logging: added some nicer logging for file path ACL checks.
102 - Audit Log: extend the commit api data with references to commit_id or pull_request_id.
103 This allows tracking those in the audit-logs.
104
105
106 Performance
107 ^^^^^^^^^^^
108
109 - Exception Tracker: optimized the check for smtp_server before doing heavy lifting
110 of exception email sending.
111 - Auth: enabled cache flags for fetching ACL ids.
112 Those are now safe to cache since we have a proper cache invalidation logic for
113 permissions of users, for lots of repo this makes our goto switcher much much faster.
114 - Application: use simpler way to extract default_user_id, this will be now registered
115 at server boot, reducing number of times we fetch this from database.
116 - Pull requests: changed reviewers metadata function for optimizing the diff calculations.
117 We're now doing a single request to calculate reviewers and diff preview instead of twice like before.
118
119
120 Fixes
121 ^^^^^
122
123 - GIT: fixed readme searcher for Git repositories using libgit2 and non-ascii directories.
124 - Full text search: fixed error while highlighting special search terms e.g 'enabled \= '
125 - Full text search: fixed problems with non-ascii files indexing.
126 - Diffs: improve text on unresolved comments attached to files that no longer exist in the review.
127 Fixes #5615.
128 - Auth: fixed generation of authomatic secrets for new plugins.
129 - Auth: failsafe github auth if it doesn't provide full name for users.
130 - Permissions: fixed problem with permissions changes from permission page due to missing cache flush.
131 This caused certain permission changed be visible after some time of the edit.
132 We now ensure *all* caches used for permissions are properly flushed right after the change.
133 - SVN: explicitly specify tunnel-user to properly map rhodecode username on svn commit
134 via SSH backend. Fixes #5608.
135 - SVN: fixed case of wrong extracted repository name for SSH backend. In cases
136 where we commit to a nested subdirs SVN reported the access path with the subdir paths in it.
137 We couldn't then match that extended name into proper rhodecode repository for ACL checks.
138 Current implementation gives an slight overhead as we have to lookup all repositories.
139 Fixes #5606
140 - SVN: fixed problem with special characters inside subdirectories.
141 - SVN: fixed SVN refs switcher on files that used old format of diff url. Fixes #5599, #5610
142 - Search: remove excessive quoting on search pagination. Fixes #5604
143 - File browser: fixed the repo switcher `?at=` flag being lost when walking on the file tree.
144 - File browser: fixed unicode problems on image preview, and make images center, no-stretch.
145 - DB migrations: fixed db migrate for latest sqlite version.
146 - Feed generator: fixed missing utc definition that could cause server 500 error.
147
148
149 Upgrade notes
150 ^^^^^^^^^^^^^
151
152 - RhodeCode has been tested on CentOS/RHEL 8 and we added those as officially supported platforms.
153 - This release introduces lots of optimizations and changes how the pull requests reviewers,
154 and diff preview is made. We cut the pull request creation time by 50%.
155 Please look closer to this new logic na report any potential problems with this change.
156 - SVN was bumped to 1.13 version. No newline at end of file
@@ -0,0 +1,60 b''
1 # Example to trigger a CI call action on specific comment text, e.g chatops and ci
2 # rebuild on mention of ci bot
3
4 @has_kwargs({
5 'repo_name': '',
6 'repo_type': '',
7 'description': '',
8 'private': '',
9 'created_on': '',
10 'enable_downloads': '',
11 'repo_id': '',
12 'user_id': '',
13 'enable_statistics': '',
14 'clone_uri': '',
15 'fork_id': '',
16 'group_id': '',
17 'created_by': '',
18 'repository': '',
19 'comment': '',
20 'commit': ''
21 })
22 def _comment_commit_repo_hook(*args, **kwargs):
23 """
24 POST CREATE REPOSITORY COMMENT ON COMMIT HOOK. This function will be executed after
25 a comment is made on this repository commit.
26
27 """
28 from .helpers import http_call, extra_fields
29 from .utils import UrlTemplate
30 # returns list of dicts with key-val fetched from extra fields
31 repo_extra_fields = extra_fields.run(**kwargs)
32
33 import rhodecode
34 from rc_integrations.jenkins_ci import csrf_call, get_auth, requests_retry_call
35
36 endpoint_url = extra_fields.get_field(
37 repo_extra_fields, key='ci_endpoint_url',
38 default='http://ci.rc.com/job/rc-ce-commits/build?COMMIT_ID=${commit}')
39 mention_text = extra_fields.get_field(
40 repo_extra_fields, key='ci_mention_text',
41 default='@jenkins build')
42
43 endpoint_url = UrlTemplate(endpoint_url).safe_substitute(
44 commit=kwargs['commit']['raw_id'])
45
46 trigger_ci = False
47 comment = kwargs['comment']['comment_text']
48 if mention_text in comment:
49 trigger_ci = True
50
51 if trigger_ci is False:
52 return HookResponse(0, '')
53
54 # call some CI based on the special coment mention marker
55 data = {
56 'project': kwargs['repository'],
57 }
58 response = http_call.run(url=endpoint_url, params=data)
59
60 return HookResponse(0, '') No newline at end of file
This diff has been collapsed as it changes many lines, (5547 lines changed) Show them Hide them
@@ -0,0 +1,5547 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 """
22 Database Models for RhodeCode Enterprise
23 """
24
25 import re
26 import os
27 import time
28 import string
29 import hashlib
30 import logging
31 import datetime
32 import uuid
33 import warnings
34 import ipaddress
35 import functools
36 import traceback
37 import collections
38
39 from sqlalchemy import (
40 or_, and_, not_, func, cast, TypeDecorator, event,
41 Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column,
42 Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary,
43 Text, Float, PickleType, BigInteger)
44 from sqlalchemy.sql.expression import true, false, case
45 from sqlalchemy.sql.functions import coalesce, count # pragma: no cover
46 from sqlalchemy.orm import (
47 relationship, joinedload, class_mapper, validates, aliased)
48 from sqlalchemy.ext.declarative import declared_attr
49 from sqlalchemy.ext.hybrid import hybrid_property
50 from sqlalchemy.exc import IntegrityError # pragma: no cover
51 from sqlalchemy.dialects.mysql import LONGTEXT
52 from zope.cachedescriptors.property import Lazy as LazyProperty
53 from pyramid import compat
54 from pyramid.threadlocal import get_current_request
55 from webhelpers2.text import remove_formatting
56
57 from rhodecode.translation import _
58 from rhodecode.lib.vcs import get_vcs_instance, VCSError
59 from rhodecode.lib.vcs.backends.base import EmptyCommit, Reference
60 from rhodecode.lib.utils2 import (
61 str2bool, safe_str, get_commit_safe, safe_unicode, sha1_safe,
62 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict,
63 glob2re, StrictAttributeDict, cleaned_uri, datetime_to_time, OrderedDefaultDict)
64 from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType, \
65 JsonRaw
66 from rhodecode.lib.ext_json import json
67 from rhodecode.lib.caching_query import FromCache
68 from rhodecode.lib.encrypt import AESCipher, validate_and_get_enc_data
69 from rhodecode.lib.encrypt2 import Encryptor
70 from rhodecode.lib.exceptions import (
71 ArtifactMetadataDuplicate, ArtifactMetadataBadValueType)
72 from rhodecode.model.meta import Base, Session
73
74 URL_SEP = '/'
75 log = logging.getLogger(__name__)
76
77 # =============================================================================
78 # BASE CLASSES
79 # =============================================================================
80
81 # this is propagated from .ini file rhodecode.encrypted_values.secret or
82 # beaker.session.secret if first is not set.
83 # and initialized at environment.py
84 ENCRYPTION_KEY = None
85
86 # used to sort permissions by types, '#' used here is not allowed to be in
87 # usernames, and it's very early in sorted string.printable table.
88 PERMISSION_TYPE_SORT = {
89 'admin': '####',
90 'write': '###',
91 'read': '##',
92 'none': '#',
93 }
94
95
96 def display_user_sort(obj):
97 """
98 Sort function used to sort permissions in .permissions() function of
99 Repository, RepoGroup, UserGroup. Also it put the default user in front
100 of all other resources
101 """
102
103 if obj.username == User.DEFAULT_USER:
104 return '#####'
105 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
106 return prefix + obj.username
107
108
109 def display_user_group_sort(obj):
110 """
111 Sort function used to sort permissions in .permissions() function of
112 Repository, RepoGroup, UserGroup. Also it put the default user in front
113 of all other resources
114 """
115
116 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
117 return prefix + obj.users_group_name
118
119
120 def _hash_key(k):
121 return sha1_safe(k)
122
123
124 def in_filter_generator(qry, items, limit=500):
125 """
126 Splits IN() into multiple with OR
127 e.g.::
128 cnt = Repository.query().filter(
129 or_(
130 *in_filter_generator(Repository.repo_id, range(100000))
131 )).count()
132 """
133 if not items:
134 # empty list will cause empty query which might cause security issues
135 # this can lead to hidden unpleasant results
136 items = [-1]
137
138 parts = []
139 for chunk in xrange(0, len(items), limit):
140 parts.append(
141 qry.in_(items[chunk: chunk + limit])
142 )
143
144 return parts
145
146
147 base_table_args = {
148 'extend_existing': True,
149 'mysql_engine': 'InnoDB',
150 'mysql_charset': 'utf8',
151 'sqlite_autoincrement': True
152 }
153
154
155 class EncryptedTextValue(TypeDecorator):
156 """
157 Special column for encrypted long text data, use like::
158
159 value = Column("encrypted_value", EncryptedValue(), nullable=False)
160
161 This column is intelligent so if value is in unencrypted form it return
162 unencrypted form, but on save it always encrypts
163 """
164 impl = Text
165
166 def process_bind_param(self, value, dialect):
167 """
168 Setter for storing value
169 """
170 import rhodecode
171 if not value:
172 return value
173
174 # protect against double encrypting if values is already encrypted
175 if value.startswith('enc$aes$') \
176 or value.startswith('enc$aes_hmac$') \
177 or value.startswith('enc2$'):
178 raise ValueError('value needs to be in unencrypted format, '
179 'ie. not starting with enc$ or enc2$')
180
181 algo = rhodecode.CONFIG.get('rhodecode.encrypted_values.algorithm') or 'aes'
182 if algo == 'aes':
183 return 'enc$aes_hmac$%s' % AESCipher(ENCRYPTION_KEY, hmac=True).encrypt(value)
184 elif algo == 'fernet':
185 return Encryptor(ENCRYPTION_KEY).encrypt(value)
186 else:
187 ValueError('Bad encryption algorithm, should be fernet or aes, got: {}'.format(algo))
188
189 def process_result_value(self, value, dialect):
190 """
191 Getter for retrieving value
192 """
193
194 import rhodecode
195 if not value:
196 return value
197
198 algo = rhodecode.CONFIG.get('rhodecode.encrypted_values.algorithm') or 'aes'
199 enc_strict_mode = str2bool(rhodecode.CONFIG.get('rhodecode.encrypted_values.strict') or True)
200 if algo == 'aes':
201 decrypted_data = validate_and_get_enc_data(value, ENCRYPTION_KEY, enc_strict_mode)
202 elif algo == 'fernet':
203 return Encryptor(ENCRYPTION_KEY).decrypt(value)
204 else:
205 ValueError('Bad encryption algorithm, should be fernet or aes, got: {}'.format(algo))
206 return decrypted_data
207
208
209 class BaseModel(object):
210 """
211 Base Model for all classes
212 """
213
214 @classmethod
215 def _get_keys(cls):
216 """return column names for this model """
217 return class_mapper(cls).c.keys()
218
219 def get_dict(self):
220 """
221 return dict with keys and values corresponding
222 to this model data """
223
224 d = {}
225 for k in self._get_keys():
226 d[k] = getattr(self, k)
227
228 # also use __json__() if present to get additional fields
229 _json_attr = getattr(self, '__json__', None)
230 if _json_attr:
231 # update with attributes from __json__
232 if callable(_json_attr):
233 _json_attr = _json_attr()
234 for k, val in _json_attr.iteritems():
235 d[k] = val
236 return d
237
238 def get_appstruct(self):
239 """return list with keys and values tuples corresponding
240 to this model data """
241
242 lst = []
243 for k in self._get_keys():
244 lst.append((k, getattr(self, k),))
245 return lst
246
247 def populate_obj(self, populate_dict):
248 """populate model with data from given populate_dict"""
249
250 for k in self._get_keys():
251 if k in populate_dict:
252 setattr(self, k, populate_dict[k])
253
254 @classmethod
255 def query(cls):
256 return Session().query(cls)
257
258 @classmethod
259 def get(cls, id_):
260 if id_:
261 return cls.query().get(id_)
262
263 @classmethod
264 def get_or_404(cls, id_):
265 from pyramid.httpexceptions import HTTPNotFound
266
267 try:
268 id_ = int(id_)
269 except (TypeError, ValueError):
270 raise HTTPNotFound()
271
272 res = cls.query().get(id_)
273 if not res:
274 raise HTTPNotFound()
275 return res
276
277 @classmethod
278 def getAll(cls):
279 # deprecated and left for backward compatibility
280 return cls.get_all()
281
282 @classmethod
283 def get_all(cls):
284 return cls.query().all()
285
286 @classmethod
287 def delete(cls, id_):
288 obj = cls.query().get(id_)
289 Session().delete(obj)
290
291 @classmethod
292 def identity_cache(cls, session, attr_name, value):
293 exist_in_session = []
294 for (item_cls, pkey), instance in session.identity_map.items():
295 if cls == item_cls and getattr(instance, attr_name) == value:
296 exist_in_session.append(instance)
297 if exist_in_session:
298 if len(exist_in_session) == 1:
299 return exist_in_session[0]
300 log.exception(
301 'multiple objects with attr %s and '
302 'value %s found with same name: %r',
303 attr_name, value, exist_in_session)
304
305 def __repr__(self):
306 if hasattr(self, '__unicode__'):
307 # python repr needs to return str
308 try:
309 return safe_str(self.__unicode__())
310 except UnicodeDecodeError:
311 pass
312 return '<DB:%s>' % (self.__class__.__name__)
313
314
315 class RhodeCodeSetting(Base, BaseModel):
316 __tablename__ = 'rhodecode_settings'
317 __table_args__ = (
318 UniqueConstraint('app_settings_name'),
319 base_table_args
320 )
321
322 SETTINGS_TYPES = {
323 'str': safe_str,
324 'int': safe_int,
325 'unicode': safe_unicode,
326 'bool': str2bool,
327 'list': functools.partial(aslist, sep=',')
328 }
329 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
330 GLOBAL_CONF_KEY = 'app_settings'
331
332 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
333 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
334 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
335 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
336
337 def __init__(self, key='', val='', type='unicode'):
338 self.app_settings_name = key
339 self.app_settings_type = type
340 self.app_settings_value = val
341
342 @validates('_app_settings_value')
343 def validate_settings_value(self, key, val):
344 assert type(val) == unicode
345 return val
346
347 @hybrid_property
348 def app_settings_value(self):
349 v = self._app_settings_value
350 _type = self.app_settings_type
351 if _type:
352 _type = self.app_settings_type.split('.')[0]
353 # decode the encrypted value
354 if 'encrypted' in self.app_settings_type:
355 cipher = EncryptedTextValue()
356 v = safe_unicode(cipher.process_result_value(v, None))
357
358 converter = self.SETTINGS_TYPES.get(_type) or \
359 self.SETTINGS_TYPES['unicode']
360 return converter(v)
361
362 @app_settings_value.setter
363 def app_settings_value(self, val):
364 """
365 Setter that will always make sure we use unicode in app_settings_value
366
367 :param val:
368 """
369 val = safe_unicode(val)
370 # encode the encrypted value
371 if 'encrypted' in self.app_settings_type:
372 cipher = EncryptedTextValue()
373 val = safe_unicode(cipher.process_bind_param(val, None))
374 self._app_settings_value = val
375
376 @hybrid_property
377 def app_settings_type(self):
378 return self._app_settings_type
379
380 @app_settings_type.setter
381 def app_settings_type(self, val):
382 if val.split('.')[0] not in self.SETTINGS_TYPES:
383 raise Exception('type must be one of %s got %s'
384 % (self.SETTINGS_TYPES.keys(), val))
385 self._app_settings_type = val
386
387 @classmethod
388 def get_by_prefix(cls, prefix):
389 return RhodeCodeSetting.query()\
390 .filter(RhodeCodeSetting.app_settings_name.startswith(prefix))\
391 .all()
392
393 def __unicode__(self):
394 return u"<%s('%s:%s[%s]')>" % (
395 self.__class__.__name__,
396 self.app_settings_name, self.app_settings_value,
397 self.app_settings_type
398 )
399
400
401 class RhodeCodeUi(Base, BaseModel):
402 __tablename__ = 'rhodecode_ui'
403 __table_args__ = (
404 UniqueConstraint('ui_key'),
405 base_table_args
406 )
407
408 HOOK_REPO_SIZE = 'changegroup.repo_size'
409 # HG
410 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
411 HOOK_PULL = 'outgoing.pull_logger'
412 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
413 HOOK_PRETX_PUSH = 'pretxnchangegroup.pre_push'
414 HOOK_PUSH = 'changegroup.push_logger'
415 HOOK_PUSH_KEY = 'pushkey.key_push'
416
417 HOOKS_BUILTIN = [
418 HOOK_PRE_PULL,
419 HOOK_PULL,
420 HOOK_PRE_PUSH,
421 HOOK_PRETX_PUSH,
422 HOOK_PUSH,
423 HOOK_PUSH_KEY,
424 ]
425
426 # TODO: johbo: Unify way how hooks are configured for git and hg,
427 # git part is currently hardcoded.
428
429 # SVN PATTERNS
430 SVN_BRANCH_ID = 'vcs_svn_branch'
431 SVN_TAG_ID = 'vcs_svn_tag'
432
433 ui_id = Column(
434 "ui_id", Integer(), nullable=False, unique=True, default=None,
435 primary_key=True)
436 ui_section = Column(
437 "ui_section", String(255), nullable=True, unique=None, default=None)
438 ui_key = Column(
439 "ui_key", String(255), nullable=True, unique=None, default=None)
440 ui_value = Column(
441 "ui_value", String(255), nullable=True, unique=None, default=None)
442 ui_active = Column(
443 "ui_active", Boolean(), nullable=True, unique=None, default=True)
444
445 def __repr__(self):
446 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
447 self.ui_key, self.ui_value)
448
449
450 class RepoRhodeCodeSetting(Base, BaseModel):
451 __tablename__ = 'repo_rhodecode_settings'
452 __table_args__ = (
453 UniqueConstraint(
454 'app_settings_name', 'repository_id',
455 name='uq_repo_rhodecode_setting_name_repo_id'),
456 base_table_args
457 )
458
459 repository_id = Column(
460 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
461 nullable=False)
462 app_settings_id = Column(
463 "app_settings_id", Integer(), nullable=False, unique=True,
464 default=None, primary_key=True)
465 app_settings_name = Column(
466 "app_settings_name", String(255), nullable=True, unique=None,
467 default=None)
468 _app_settings_value = Column(
469 "app_settings_value", String(4096), nullable=True, unique=None,
470 default=None)
471 _app_settings_type = Column(
472 "app_settings_type", String(255), nullable=True, unique=None,
473 default=None)
474
475 repository = relationship('Repository')
476
477 def __init__(self, repository_id, key='', val='', type='unicode'):
478 self.repository_id = repository_id
479 self.app_settings_name = key
480 self.app_settings_type = type
481 self.app_settings_value = val
482
483 @validates('_app_settings_value')
484 def validate_settings_value(self, key, val):
485 assert type(val) == unicode
486 return val
487
488 @hybrid_property
489 def app_settings_value(self):
490 v = self._app_settings_value
491 type_ = self.app_settings_type
492 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
493 converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode']
494 return converter(v)
495
496 @app_settings_value.setter
497 def app_settings_value(self, val):
498 """
499 Setter that will always make sure we use unicode in app_settings_value
500
501 :param val:
502 """
503 self._app_settings_value = safe_unicode(val)
504
505 @hybrid_property
506 def app_settings_type(self):
507 return self._app_settings_type
508
509 @app_settings_type.setter
510 def app_settings_type(self, val):
511 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
512 if val not in SETTINGS_TYPES:
513 raise Exception('type must be one of %s got %s'
514 % (SETTINGS_TYPES.keys(), val))
515 self._app_settings_type = val
516
517 def __unicode__(self):
518 return u"<%s('%s:%s:%s[%s]')>" % (
519 self.__class__.__name__, self.repository.repo_name,
520 self.app_settings_name, self.app_settings_value,
521 self.app_settings_type
522 )
523
524
525 class RepoRhodeCodeUi(Base, BaseModel):
526 __tablename__ = 'repo_rhodecode_ui'
527 __table_args__ = (
528 UniqueConstraint(
529 'repository_id', 'ui_section', 'ui_key',
530 name='uq_repo_rhodecode_ui_repository_id_section_key'),
531 base_table_args
532 )
533
534 repository_id = Column(
535 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
536 nullable=False)
537 ui_id = Column(
538 "ui_id", Integer(), nullable=False, unique=True, default=None,
539 primary_key=True)
540 ui_section = Column(
541 "ui_section", String(255), nullable=True, unique=None, default=None)
542 ui_key = Column(
543 "ui_key", String(255), nullable=True, unique=None, default=None)
544 ui_value = Column(
545 "ui_value", String(255), nullable=True, unique=None, default=None)
546 ui_active = Column(
547 "ui_active", Boolean(), nullable=True, unique=None, default=True)
548
549 repository = relationship('Repository')
550
551 def __repr__(self):
552 return '<%s[%s:%s]%s=>%s]>' % (
553 self.__class__.__name__, self.repository.repo_name,
554 self.ui_section, self.ui_key, self.ui_value)
555
556
557 class User(Base, BaseModel):
558 __tablename__ = 'users'
559 __table_args__ = (
560 UniqueConstraint('username'), UniqueConstraint('email'),
561 Index('u_username_idx', 'username'),
562 Index('u_email_idx', 'email'),
563 base_table_args
564 )
565
566 DEFAULT_USER = 'default'
567 DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org'
568 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
569
570 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
571 username = Column("username", String(255), nullable=True, unique=None, default=None)
572 password = Column("password", String(255), nullable=True, unique=None, default=None)
573 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
574 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
575 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
576 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
577 _email = Column("email", String(255), nullable=True, unique=None, default=None)
578 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
579 last_activity = Column('last_activity', DateTime(timezone=False), nullable=True, unique=None, default=None)
580 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
581
582 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
583 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
584 _api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
585 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
586 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
587 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
588
589 user_log = relationship('UserLog')
590 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all, delete-orphan')
591
592 repositories = relationship('Repository')
593 repository_groups = relationship('RepoGroup')
594 user_groups = relationship('UserGroup')
595
596 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
597 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
598
599 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all, delete-orphan')
600 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all, delete-orphan')
601 user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all, delete-orphan')
602
603 group_member = relationship('UserGroupMember', cascade='all')
604
605 notifications = relationship('UserNotification', cascade='all')
606 # notifications assigned to this user
607 user_created_notifications = relationship('Notification', cascade='all')
608 # comments created by this user
609 user_comments = relationship('ChangesetComment', cascade='all')
610 # user profile extra info
611 user_emails = relationship('UserEmailMap', cascade='all')
612 user_ip_map = relationship('UserIpMap', cascade='all')
613 user_auth_tokens = relationship('UserApiKeys', cascade='all')
614 user_ssh_keys = relationship('UserSshKeys', cascade='all')
615
616 # gists
617 user_gists = relationship('Gist', cascade='all')
618 # user pull requests
619 user_pull_requests = relationship('PullRequest', cascade='all')
620 # external identities
621 external_identities = relationship(
622 'ExternalIdentity',
623 primaryjoin="User.user_id==ExternalIdentity.local_user_id",
624 cascade='all')
625 # review rules
626 user_review_rules = relationship('RepoReviewRuleUser', cascade='all')
627
628 # artifacts owned
629 artifacts = relationship('FileStore', primaryjoin='FileStore.user_id==User.user_id')
630
631 # no cascade, set NULL
632 scope_artifacts = relationship('FileStore', primaryjoin='FileStore.scope_user_id==User.user_id')
633
634 def __unicode__(self):
635 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
636 self.user_id, self.username)
637
638 @hybrid_property
639 def email(self):
640 return self._email
641
642 @email.setter
643 def email(self, val):
644 self._email = val.lower() if val else None
645
646 @hybrid_property
647 def first_name(self):
648 from rhodecode.lib import helpers as h
649 if self.name:
650 return h.escape(self.name)
651 return self.name
652
653 @hybrid_property
654 def last_name(self):
655 from rhodecode.lib import helpers as h
656 if self.lastname:
657 return h.escape(self.lastname)
658 return self.lastname
659
660 @hybrid_property
661 def api_key(self):
662 """
663 Fetch if exist an auth-token with role ALL connected to this user
664 """
665 user_auth_token = UserApiKeys.query()\
666 .filter(UserApiKeys.user_id == self.user_id)\
667 .filter(or_(UserApiKeys.expires == -1,
668 UserApiKeys.expires >= time.time()))\
669 .filter(UserApiKeys.role == UserApiKeys.ROLE_ALL).first()
670 if user_auth_token:
671 user_auth_token = user_auth_token.api_key
672
673 return user_auth_token
674
675 @api_key.setter
676 def api_key(self, val):
677 # don't allow to set API key this is deprecated for now
678 self._api_key = None
679
680 @property
681 def reviewer_pull_requests(self):
682 return PullRequestReviewers.query() \
683 .options(joinedload(PullRequestReviewers.pull_request)) \
684 .filter(PullRequestReviewers.user_id == self.user_id) \
685 .all()
686
687 @property
688 def firstname(self):
689 # alias for future
690 return self.name
691
692 @property
693 def emails(self):
694 other = UserEmailMap.query()\
695 .filter(UserEmailMap.user == self) \
696 .order_by(UserEmailMap.email_id.asc()) \
697 .all()
698 return [self.email] + [x.email for x in other]
699
700 def emails_cached(self):
701 emails = UserEmailMap.query()\
702 .filter(UserEmailMap.user == self) \
703 .order_by(UserEmailMap.email_id.asc())
704
705 emails = emails.options(
706 FromCache("sql_cache_short", "get_user_{}_emails".format(self.user_id))
707 )
708
709 return [self.email] + [x.email for x in emails]
710
711 @property
712 def auth_tokens(self):
713 auth_tokens = self.get_auth_tokens()
714 return [x.api_key for x in auth_tokens]
715
716 def get_auth_tokens(self):
717 return UserApiKeys.query()\
718 .filter(UserApiKeys.user == self)\
719 .order_by(UserApiKeys.user_api_key_id.asc())\
720 .all()
721
722 @LazyProperty
723 def feed_token(self):
724 return self.get_feed_token()
725
726 def get_feed_token(self, cache=True):
727 feed_tokens = UserApiKeys.query()\
728 .filter(UserApiKeys.user == self)\
729 .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)
730 if cache:
731 feed_tokens = feed_tokens.options(
732 FromCache("sql_cache_short", "get_user_feed_token_%s" % self.user_id))
733
734 feed_tokens = feed_tokens.all()
735 if feed_tokens:
736 return feed_tokens[0].api_key
737 return 'NO_FEED_TOKEN_AVAILABLE'
738
739 @LazyProperty
740 def artifact_token(self):
741 return self.get_artifact_token()
742
743 def get_artifact_token(self, cache=True):
744 artifacts_tokens = UserApiKeys.query()\
745 .filter(UserApiKeys.user == self)\
746 .filter(UserApiKeys.role == UserApiKeys.ROLE_ARTIFACT_DOWNLOAD)
747 if cache:
748 artifacts_tokens = artifacts_tokens.options(
749 FromCache("sql_cache_short", "get_user_artifact_token_%s" % self.user_id))
750
751 artifacts_tokens = artifacts_tokens.all()
752 if artifacts_tokens:
753 return artifacts_tokens[0].api_key
754 return 'NO_ARTIFACT_TOKEN_AVAILABLE'
755
756 @classmethod
757 def get(cls, user_id, cache=False):
758 if not user_id:
759 return
760
761 user = cls.query()
762 if cache:
763 user = user.options(
764 FromCache("sql_cache_short", "get_users_%s" % user_id))
765 return user.get(user_id)
766
767 @classmethod
768 def extra_valid_auth_tokens(cls, user, role=None):
769 tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\
770 .filter(or_(UserApiKeys.expires == -1,
771 UserApiKeys.expires >= time.time()))
772 if role:
773 tokens = tokens.filter(or_(UserApiKeys.role == role,
774 UserApiKeys.role == UserApiKeys.ROLE_ALL))
775 return tokens.all()
776
777 def authenticate_by_token(self, auth_token, roles=None, scope_repo_id=None):
778 from rhodecode.lib import auth
779
780 log.debug('Trying to authenticate user: %s via auth-token, '
781 'and roles: %s', self, roles)
782
783 if not auth_token:
784 return False
785
786 roles = (roles or []) + [UserApiKeys.ROLE_ALL]
787 tokens_q = UserApiKeys.query()\
788 .filter(UserApiKeys.user_id == self.user_id)\
789 .filter(or_(UserApiKeys.expires == -1,
790 UserApiKeys.expires >= time.time()))
791
792 tokens_q = tokens_q.filter(UserApiKeys.role.in_(roles))
793
794 crypto_backend = auth.crypto_backend()
795 enc_token_map = {}
796 plain_token_map = {}
797 for token in tokens_q:
798 if token.api_key.startswith(crypto_backend.ENC_PREF):
799 enc_token_map[token.api_key] = token
800 else:
801 plain_token_map[token.api_key] = token
802 log.debug(
803 'Found %s plain and %s encrypted tokens to check for authentication for this user',
804 len(plain_token_map), len(enc_token_map))
805
806 # plain token match comes first
807 match = plain_token_map.get(auth_token)
808
809 # check encrypted tokens now
810 if not match:
811 for token_hash, token in enc_token_map.items():
812 # NOTE(marcink): this is expensive to calculate, but most secure
813 if crypto_backend.hash_check(auth_token, token_hash):
814 match = token
815 break
816
817 if match:
818 log.debug('Found matching token %s', match)
819 if match.repo_id:
820 log.debug('Found scope, checking for scope match of token %s', match)
821 if match.repo_id == scope_repo_id:
822 return True
823 else:
824 log.debug(
825 'AUTH_TOKEN: scope mismatch, token has a set repo scope: %s, '
826 'and calling scope is:%s, skipping further checks',
827 match.repo, scope_repo_id)
828 return False
829 else:
830 return True
831
832 return False
833
834 @property
835 def ip_addresses(self):
836 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
837 return [x.ip_addr for x in ret]
838
839 @property
840 def username_and_name(self):
841 return '%s (%s %s)' % (self.username, self.first_name, self.last_name)
842
843 @property
844 def username_or_name_or_email(self):
845 full_name = self.full_name if self.full_name is not ' ' else None
846 return self.username or full_name or self.email
847
848 @property
849 def full_name(self):
850 return '%s %s' % (self.first_name, self.last_name)
851
852 @property
853 def full_name_or_username(self):
854 return ('%s %s' % (self.first_name, self.last_name)
855 if (self.first_name and self.last_name) else self.username)
856
857 @property
858 def full_contact(self):
859 return '%s %s <%s>' % (self.first_name, self.last_name, self.email)
860
861 @property
862 def short_contact(self):
863 return '%s %s' % (self.first_name, self.last_name)
864
865 @property
866 def is_admin(self):
867 return self.admin
868
869 @property
870 def language(self):
871 return self.user_data.get('language')
872
873 def AuthUser(self, **kwargs):
874 """
875 Returns instance of AuthUser for this user
876 """
877 from rhodecode.lib.auth import AuthUser
878 return AuthUser(user_id=self.user_id, username=self.username, **kwargs)
879
880 @hybrid_property
881 def user_data(self):
882 if not self._user_data:
883 return {}
884
885 try:
886 return json.loads(self._user_data)
887 except TypeError:
888 return {}
889
890 @user_data.setter
891 def user_data(self, val):
892 if not isinstance(val, dict):
893 raise Exception('user_data must be dict, got %s' % type(val))
894 try:
895 self._user_data = json.dumps(val)
896 except Exception:
897 log.error(traceback.format_exc())
898
899 @classmethod
900 def get_by_username(cls, username, case_insensitive=False,
901 cache=False, identity_cache=False):
902 session = Session()
903
904 if case_insensitive:
905 q = cls.query().filter(
906 func.lower(cls.username) == func.lower(username))
907 else:
908 q = cls.query().filter(cls.username == username)
909
910 if cache:
911 if identity_cache:
912 val = cls.identity_cache(session, 'username', username)
913 if val:
914 return val
915 else:
916 cache_key = "get_user_by_name_%s" % _hash_key(username)
917 q = q.options(
918 FromCache("sql_cache_short", cache_key))
919
920 return q.scalar()
921
922 @classmethod
923 def get_by_auth_token(cls, auth_token, cache=False):
924 q = UserApiKeys.query()\
925 .filter(UserApiKeys.api_key == auth_token)\
926 .filter(or_(UserApiKeys.expires == -1,
927 UserApiKeys.expires >= time.time()))
928 if cache:
929 q = q.options(
930 FromCache("sql_cache_short", "get_auth_token_%s" % auth_token))
931
932 match = q.first()
933 if match:
934 return match.user
935
936 @classmethod
937 def get_by_email(cls, email, case_insensitive=False, cache=False):
938
939 if case_insensitive:
940 q = cls.query().filter(func.lower(cls.email) == func.lower(email))
941
942 else:
943 q = cls.query().filter(cls.email == email)
944
945 email_key = _hash_key(email)
946 if cache:
947 q = q.options(
948 FromCache("sql_cache_short", "get_email_key_%s" % email_key))
949
950 ret = q.scalar()
951 if ret is None:
952 q = UserEmailMap.query()
953 # try fetching in alternate email map
954 if case_insensitive:
955 q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
956 else:
957 q = q.filter(UserEmailMap.email == email)
958 q = q.options(joinedload(UserEmailMap.user))
959 if cache:
960 q = q.options(
961 FromCache("sql_cache_short", "get_email_map_key_%s" % email_key))
962 ret = getattr(q.scalar(), 'user', None)
963
964 return ret
965
966 @classmethod
967 def get_from_cs_author(cls, author):
968 """
969 Tries to get User objects out of commit author string
970
971 :param author:
972 """
973 from rhodecode.lib.helpers import email, author_name
974 # Valid email in the attribute passed, see if they're in the system
975 _email = email(author)
976 if _email:
977 user = cls.get_by_email(_email, case_insensitive=True)
978 if user:
979 return user
980 # Maybe we can match by username?
981 _author = author_name(author)
982 user = cls.get_by_username(_author, case_insensitive=True)
983 if user:
984 return user
985
986 def update_userdata(self, **kwargs):
987 usr = self
988 old = usr.user_data
989 old.update(**kwargs)
990 usr.user_data = old
991 Session().add(usr)
992 log.debug('updated userdata with %s', kwargs)
993
994 def update_lastlogin(self):
995 """Update user lastlogin"""
996 self.last_login = datetime.datetime.now()
997 Session().add(self)
998 log.debug('updated user %s lastlogin', self.username)
999
1000 def update_password(self, new_password):
1001 from rhodecode.lib.auth import get_crypt_password
1002
1003 self.password = get_crypt_password(new_password)
1004 Session().add(self)
1005
1006 @classmethod
1007 def get_first_super_admin(cls):
1008 user = User.query()\
1009 .filter(User.admin == true()) \
1010 .order_by(User.user_id.asc()) \
1011 .first()
1012
1013 if user is None:
1014 raise Exception('FATAL: Missing administrative account!')
1015 return user
1016
1017 @classmethod
1018 def get_all_super_admins(cls, only_active=False):
1019 """
1020 Returns all admin accounts sorted by username
1021 """
1022 qry = User.query().filter(User.admin == true()).order_by(User.username.asc())
1023 if only_active:
1024 qry = qry.filter(User.active == true())
1025 return qry.all()
1026
1027 @classmethod
1028 def get_all_user_ids(cls, only_active=True):
1029 """
1030 Returns all users IDs
1031 """
1032 qry = Session().query(User.user_id)
1033
1034 if only_active:
1035 qry = qry.filter(User.active == true())
1036 return [x.user_id for x in qry]
1037
1038 @classmethod
1039 def get_default_user(cls, cache=False, refresh=False):
1040 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
1041 if user is None:
1042 raise Exception('FATAL: Missing default account!')
1043 if refresh:
1044 # The default user might be based on outdated state which
1045 # has been loaded from the cache.
1046 # A call to refresh() ensures that the
1047 # latest state from the database is used.
1048 Session().refresh(user)
1049 return user
1050
1051 def _get_default_perms(self, user, suffix=''):
1052 from rhodecode.model.permission import PermissionModel
1053 return PermissionModel().get_default_perms(user.user_perms, suffix)
1054
1055 def get_default_perms(self, suffix=''):
1056 return self._get_default_perms(self, suffix)
1057
1058 def get_api_data(self, include_secrets=False, details='full'):
1059 """
1060 Common function for generating user related data for API
1061
1062 :param include_secrets: By default secrets in the API data will be replaced
1063 by a placeholder value to prevent exposing this data by accident. In case
1064 this data shall be exposed, set this flag to ``True``.
1065
1066 :param details: details can be 'basic|full' basic gives only a subset of
1067 the available user information that includes user_id, name and emails.
1068 """
1069 user = self
1070 user_data = self.user_data
1071 data = {
1072 'user_id': user.user_id,
1073 'username': user.username,
1074 'firstname': user.name,
1075 'lastname': user.lastname,
1076 'description': user.description,
1077 'email': user.email,
1078 'emails': user.emails,
1079 }
1080 if details == 'basic':
1081 return data
1082
1083 auth_token_length = 40
1084 auth_token_replacement = '*' * auth_token_length
1085
1086 extras = {
1087 'auth_tokens': [auth_token_replacement],
1088 'active': user.active,
1089 'admin': user.admin,
1090 'extern_type': user.extern_type,
1091 'extern_name': user.extern_name,
1092 'last_login': user.last_login,
1093 'last_activity': user.last_activity,
1094 'ip_addresses': user.ip_addresses,
1095 'language': user_data.get('language')
1096 }
1097 data.update(extras)
1098
1099 if include_secrets:
1100 data['auth_tokens'] = user.auth_tokens
1101 return data
1102
1103 def __json__(self):
1104 data = {
1105 'full_name': self.full_name,
1106 'full_name_or_username': self.full_name_or_username,
1107 'short_contact': self.short_contact,
1108 'full_contact': self.full_contact,
1109 }
1110 data.update(self.get_api_data())
1111 return data
1112
1113
1114 class UserApiKeys(Base, BaseModel):
1115 __tablename__ = 'user_api_keys'
1116 __table_args__ = (
1117 Index('uak_api_key_idx', 'api_key'),
1118 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
1119 base_table_args
1120 )
1121 __mapper_args__ = {}
1122
1123 # ApiKey role
1124 ROLE_ALL = 'token_role_all'
1125 ROLE_HTTP = 'token_role_http'
1126 ROLE_VCS = 'token_role_vcs'
1127 ROLE_API = 'token_role_api'
1128 ROLE_FEED = 'token_role_feed'
1129 ROLE_ARTIFACT_DOWNLOAD = 'role_artifact_download'
1130 ROLE_PASSWORD_RESET = 'token_password_reset'
1131
1132 ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED, ROLE_ARTIFACT_DOWNLOAD]
1133
1134 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1135 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1136 api_key = Column("api_key", String(255), nullable=False, unique=True)
1137 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1138 expires = Column('expires', Float(53), nullable=False)
1139 role = Column('role', String(255), nullable=True)
1140 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1141
1142 # scope columns
1143 repo_id = Column(
1144 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
1145 nullable=True, unique=None, default=None)
1146 repo = relationship('Repository', lazy='joined')
1147
1148 repo_group_id = Column(
1149 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
1150 nullable=True, unique=None, default=None)
1151 repo_group = relationship('RepoGroup', lazy='joined')
1152
1153 user = relationship('User', lazy='joined')
1154
1155 def __unicode__(self):
1156 return u"<%s('%s')>" % (self.__class__.__name__, self.role)
1157
1158 def __json__(self):
1159 data = {
1160 'auth_token': self.api_key,
1161 'role': self.role,
1162 'scope': self.scope_humanized,
1163 'expired': self.expired
1164 }
1165 return data
1166
1167 def get_api_data(self, include_secrets=False):
1168 data = self.__json__()
1169 if include_secrets:
1170 return data
1171 else:
1172 data['auth_token'] = self.token_obfuscated
1173 return data
1174
1175 @hybrid_property
1176 def description_safe(self):
1177 from rhodecode.lib import helpers as h
1178 return h.escape(self.description)
1179
1180 @property
1181 def expired(self):
1182 if self.expires == -1:
1183 return False
1184 return time.time() > self.expires
1185
1186 @classmethod
1187 def _get_role_name(cls, role):
1188 return {
1189 cls.ROLE_ALL: _('all'),
1190 cls.ROLE_HTTP: _('http/web interface'),
1191 cls.ROLE_VCS: _('vcs (git/hg/svn protocol)'),
1192 cls.ROLE_API: _('api calls'),
1193 cls.ROLE_FEED: _('feed access'),
1194 cls.ROLE_ARTIFACT_DOWNLOAD: _('artifacts downloads'),
1195 }.get(role, role)
1196
1197 @property
1198 def role_humanized(self):
1199 return self._get_role_name(self.role)
1200
1201 def _get_scope(self):
1202 if self.repo:
1203 return 'Repository: {}'.format(self.repo.repo_name)
1204 if self.repo_group:
1205 return 'RepositoryGroup: {} (recursive)'.format(self.repo_group.group_name)
1206 return 'Global'
1207
1208 @property
1209 def scope_humanized(self):
1210 return self._get_scope()
1211
1212 @property
1213 def token_obfuscated(self):
1214 if self.api_key:
1215 return self.api_key[:4] + "****"
1216
1217
1218 class UserEmailMap(Base, BaseModel):
1219 __tablename__ = 'user_email_map'
1220 __table_args__ = (
1221 Index('uem_email_idx', 'email'),
1222 UniqueConstraint('email'),
1223 base_table_args
1224 )
1225 __mapper_args__ = {}
1226
1227 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1228 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1229 _email = Column("email", String(255), nullable=True, unique=False, default=None)
1230 user = relationship('User', lazy='joined')
1231
1232 @validates('_email')
1233 def validate_email(self, key, email):
1234 # check if this email is not main one
1235 main_email = Session().query(User).filter(User.email == email).scalar()
1236 if main_email is not None:
1237 raise AttributeError('email %s is present is user table' % email)
1238 return email
1239
1240 @hybrid_property
1241 def email(self):
1242 return self._email
1243
1244 @email.setter
1245 def email(self, val):
1246 self._email = val.lower() if val else None
1247
1248
1249 class UserIpMap(Base, BaseModel):
1250 __tablename__ = 'user_ip_map'
1251 __table_args__ = (
1252 UniqueConstraint('user_id', 'ip_addr'),
1253 base_table_args
1254 )
1255 __mapper_args__ = {}
1256
1257 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1258 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1259 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
1260 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
1261 description = Column("description", String(10000), nullable=True, unique=None, default=None)
1262 user = relationship('User', lazy='joined')
1263
1264 @hybrid_property
1265 def description_safe(self):
1266 from rhodecode.lib import helpers as h
1267 return h.escape(self.description)
1268
1269 @classmethod
1270 def _get_ip_range(cls, ip_addr):
1271 net = ipaddress.ip_network(safe_unicode(ip_addr), strict=False)
1272 return [str(net.network_address), str(net.broadcast_address)]
1273
1274 def __json__(self):
1275 return {
1276 'ip_addr': self.ip_addr,
1277 'ip_range': self._get_ip_range(self.ip_addr),
1278 }
1279
1280 def __unicode__(self):
1281 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
1282 self.user_id, self.ip_addr)
1283
1284
1285 class UserSshKeys(Base, BaseModel):
1286 __tablename__ = 'user_ssh_keys'
1287 __table_args__ = (
1288 Index('usk_ssh_key_fingerprint_idx', 'ssh_key_fingerprint'),
1289
1290 UniqueConstraint('ssh_key_fingerprint'),
1291
1292 base_table_args
1293 )
1294 __mapper_args__ = {}
1295
1296 ssh_key_id = Column('ssh_key_id', Integer(), nullable=False, unique=True, default=None, primary_key=True)
1297 ssh_key_data = Column('ssh_key_data', String(10240), nullable=False, unique=None, default=None)
1298 ssh_key_fingerprint = Column('ssh_key_fingerprint', String(255), nullable=False, unique=None, default=None)
1299
1300 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1301
1302 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1303 accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True, default=None)
1304 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1305
1306 user = relationship('User', lazy='joined')
1307
1308 def __json__(self):
1309 data = {
1310 'ssh_fingerprint': self.ssh_key_fingerprint,
1311 'description': self.description,
1312 'created_on': self.created_on
1313 }
1314 return data
1315
1316 def get_api_data(self):
1317 data = self.__json__()
1318 return data
1319
1320
1321 class UserLog(Base, BaseModel):
1322 __tablename__ = 'user_logs'
1323 __table_args__ = (
1324 base_table_args,
1325 )
1326
1327 VERSION_1 = 'v1'
1328 VERSION_2 = 'v2'
1329 VERSIONS = [VERSION_1, VERSION_2]
1330
1331 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1332 user_id = Column("user_id", Integer(), ForeignKey('users.user_id',ondelete='SET NULL'), nullable=True, unique=None, default=None)
1333 username = Column("username", String(255), nullable=True, unique=None, default=None)
1334 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id', ondelete='SET NULL'), nullable=True, unique=None, default=None)
1335 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
1336 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
1337 action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
1338 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
1339
1340 version = Column("version", String(255), nullable=True, default=VERSION_1)
1341 user_data = Column('user_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=LONGTEXT()))))
1342 action_data = Column('action_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=LONGTEXT()))))
1343
1344 def __unicode__(self):
1345 return u"<%s('id:%s:%s')>" % (
1346 self.__class__.__name__, self.repository_name, self.action)
1347
1348 def __json__(self):
1349 return {
1350 'user_id': self.user_id,
1351 'username': self.username,
1352 'repository_id': self.repository_id,
1353 'repository_name': self.repository_name,
1354 'user_ip': self.user_ip,
1355 'action_date': self.action_date,
1356 'action': self.action,
1357 }
1358
1359 @hybrid_property
1360 def entry_id(self):
1361 return self.user_log_id
1362
1363 @property
1364 def action_as_day(self):
1365 return datetime.date(*self.action_date.timetuple()[:3])
1366
1367 user = relationship('User')
1368 repository = relationship('Repository', cascade='')
1369
1370
1371 class UserGroup(Base, BaseModel):
1372 __tablename__ = 'users_groups'
1373 __table_args__ = (
1374 base_table_args,
1375 )
1376
1377 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1378 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
1379 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
1380 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
1381 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
1382 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1383 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1384 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
1385
1386 members = relationship('UserGroupMember', cascade="all, delete-orphan", lazy="joined")
1387 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
1388 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1389 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1390 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1391 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1392
1393 user_group_review_rules = relationship('RepoReviewRuleUserGroup', cascade='all')
1394 user = relationship('User', primaryjoin="User.user_id==UserGroup.user_id")
1395
1396 @classmethod
1397 def _load_group_data(cls, column):
1398 if not column:
1399 return {}
1400
1401 try:
1402 return json.loads(column) or {}
1403 except TypeError:
1404 return {}
1405
1406 @hybrid_property
1407 def description_safe(self):
1408 from rhodecode.lib import helpers as h
1409 return h.escape(self.user_group_description)
1410
1411 @hybrid_property
1412 def group_data(self):
1413 return self._load_group_data(self._group_data)
1414
1415 @group_data.expression
1416 def group_data(self, **kwargs):
1417 return self._group_data
1418
1419 @group_data.setter
1420 def group_data(self, val):
1421 try:
1422 self._group_data = json.dumps(val)
1423 except Exception:
1424 log.error(traceback.format_exc())
1425
1426 @classmethod
1427 def _load_sync(cls, group_data):
1428 if group_data:
1429 return group_data.get('extern_type')
1430
1431 @property
1432 def sync(self):
1433 return self._load_sync(self.group_data)
1434
1435 def __unicode__(self):
1436 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
1437 self.users_group_id,
1438 self.users_group_name)
1439
1440 @classmethod
1441 def get_by_group_name(cls, group_name, cache=False,
1442 case_insensitive=False):
1443 if case_insensitive:
1444 q = cls.query().filter(func.lower(cls.users_group_name) ==
1445 func.lower(group_name))
1446
1447 else:
1448 q = cls.query().filter(cls.users_group_name == group_name)
1449 if cache:
1450 q = q.options(
1451 FromCache("sql_cache_short", "get_group_%s" % _hash_key(group_name)))
1452 return q.scalar()
1453
1454 @classmethod
1455 def get(cls, user_group_id, cache=False):
1456 if not user_group_id:
1457 return
1458
1459 user_group = cls.query()
1460 if cache:
1461 user_group = user_group.options(
1462 FromCache("sql_cache_short", "get_users_group_%s" % user_group_id))
1463 return user_group.get(user_group_id)
1464
1465 def permissions(self, with_admins=True, with_owner=True,
1466 expand_from_user_groups=False):
1467 """
1468 Permissions for user groups
1469 """
1470 _admin_perm = 'usergroup.admin'
1471
1472 owner_row = []
1473 if with_owner:
1474 usr = AttributeDict(self.user.get_dict())
1475 usr.owner_row = True
1476 usr.permission = _admin_perm
1477 owner_row.append(usr)
1478
1479 super_admin_ids = []
1480 super_admin_rows = []
1481 if with_admins:
1482 for usr in User.get_all_super_admins():
1483 super_admin_ids.append(usr.user_id)
1484 # if this admin is also owner, don't double the record
1485 if usr.user_id == owner_row[0].user_id:
1486 owner_row[0].admin_row = True
1487 else:
1488 usr = AttributeDict(usr.get_dict())
1489 usr.admin_row = True
1490 usr.permission = _admin_perm
1491 super_admin_rows.append(usr)
1492
1493 q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self)
1494 q = q.options(joinedload(UserUserGroupToPerm.user_group),
1495 joinedload(UserUserGroupToPerm.user),
1496 joinedload(UserUserGroupToPerm.permission),)
1497
1498 # get owners and admins and permissions. We do a trick of re-writing
1499 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1500 # has a global reference and changing one object propagates to all
1501 # others. This means if admin is also an owner admin_row that change
1502 # would propagate to both objects
1503 perm_rows = []
1504 for _usr in q.all():
1505 usr = AttributeDict(_usr.user.get_dict())
1506 # if this user is also owner/admin, mark as duplicate record
1507 if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids:
1508 usr.duplicate_perm = True
1509 usr.permission = _usr.permission.permission_name
1510 perm_rows.append(usr)
1511
1512 # filter the perm rows by 'default' first and then sort them by
1513 # admin,write,read,none permissions sorted again alphabetically in
1514 # each group
1515 perm_rows = sorted(perm_rows, key=display_user_sort)
1516
1517 user_groups_rows = []
1518 if expand_from_user_groups:
1519 for ug in self.permission_user_groups(with_members=True):
1520 for user_data in ug.members:
1521 user_groups_rows.append(user_data)
1522
1523 return super_admin_rows + owner_row + perm_rows + user_groups_rows
1524
1525 def permission_user_groups(self, with_members=False):
1526 q = UserGroupUserGroupToPerm.query()\
1527 .filter(UserGroupUserGroupToPerm.target_user_group == self)
1528 q = q.options(joinedload(UserGroupUserGroupToPerm.user_group),
1529 joinedload(UserGroupUserGroupToPerm.target_user_group),
1530 joinedload(UserGroupUserGroupToPerm.permission),)
1531
1532 perm_rows = []
1533 for _user_group in q.all():
1534 entry = AttributeDict(_user_group.user_group.get_dict())
1535 entry.permission = _user_group.permission.permission_name
1536 if with_members:
1537 entry.members = [x.user.get_dict()
1538 for x in _user_group.user_group.members]
1539 perm_rows.append(entry)
1540
1541 perm_rows = sorted(perm_rows, key=display_user_group_sort)
1542 return perm_rows
1543
1544 def _get_default_perms(self, user_group, suffix=''):
1545 from rhodecode.model.permission import PermissionModel
1546 return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix)
1547
1548 def get_default_perms(self, suffix=''):
1549 return self._get_default_perms(self, suffix)
1550
1551 def get_api_data(self, with_group_members=True, include_secrets=False):
1552 """
1553 :param include_secrets: See :meth:`User.get_api_data`, this parameter is
1554 basically forwarded.
1555
1556 """
1557 user_group = self
1558 data = {
1559 'users_group_id': user_group.users_group_id,
1560 'group_name': user_group.users_group_name,
1561 'group_description': user_group.user_group_description,
1562 'active': user_group.users_group_active,
1563 'owner': user_group.user.username,
1564 'sync': user_group.sync,
1565 'owner_email': user_group.user.email,
1566 }
1567
1568 if with_group_members:
1569 users = []
1570 for user in user_group.members:
1571 user = user.user
1572 users.append(user.get_api_data(include_secrets=include_secrets))
1573 data['users'] = users
1574
1575 return data
1576
1577
1578 class UserGroupMember(Base, BaseModel):
1579 __tablename__ = 'users_groups_members'
1580 __table_args__ = (
1581 base_table_args,
1582 )
1583
1584 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1585 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1586 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1587
1588 user = relationship('User', lazy='joined')
1589 users_group = relationship('UserGroup')
1590
1591 def __init__(self, gr_id='', u_id=''):
1592 self.users_group_id = gr_id
1593 self.user_id = u_id
1594
1595
1596 class RepositoryField(Base, BaseModel):
1597 __tablename__ = 'repositories_fields'
1598 __table_args__ = (
1599 UniqueConstraint('repository_id', 'field_key'), # no-multi field
1600 base_table_args,
1601 )
1602
1603 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
1604
1605 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1606 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1607 field_key = Column("field_key", String(250))
1608 field_label = Column("field_label", String(1024), nullable=False)
1609 field_value = Column("field_value", String(10000), nullable=False)
1610 field_desc = Column("field_desc", String(1024), nullable=False)
1611 field_type = Column("field_type", String(255), nullable=False, unique=None)
1612 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1613
1614 repository = relationship('Repository')
1615
1616 @property
1617 def field_key_prefixed(self):
1618 return 'ex_%s' % self.field_key
1619
1620 @classmethod
1621 def un_prefix_key(cls, key):
1622 if key.startswith(cls.PREFIX):
1623 return key[len(cls.PREFIX):]
1624 return key
1625
1626 @classmethod
1627 def get_by_key_name(cls, key, repo):
1628 row = cls.query()\
1629 .filter(cls.repository == repo)\
1630 .filter(cls.field_key == key).scalar()
1631 return row
1632
1633
1634 class Repository(Base, BaseModel):
1635 __tablename__ = 'repositories'
1636 __table_args__ = (
1637 Index('r_repo_name_idx', 'repo_name', mysql_length=255),
1638 base_table_args,
1639 )
1640 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
1641 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
1642 DEFAULT_CLONE_URI_SSH = 'ssh://{sys_user}@{hostname}/{repo}'
1643
1644 STATE_CREATED = 'repo_state_created'
1645 STATE_PENDING = 'repo_state_pending'
1646 STATE_ERROR = 'repo_state_error'
1647
1648 LOCK_AUTOMATIC = 'lock_auto'
1649 LOCK_API = 'lock_api'
1650 LOCK_WEB = 'lock_web'
1651 LOCK_PULL = 'lock_pull'
1652
1653 NAME_SEP = URL_SEP
1654
1655 repo_id = Column(
1656 "repo_id", Integer(), nullable=False, unique=True, default=None,
1657 primary_key=True)
1658 _repo_name = Column(
1659 "repo_name", Text(), nullable=False, default=None)
1660 repo_name_hash = Column(
1661 "repo_name_hash", String(255), nullable=False, unique=True)
1662 repo_state = Column("repo_state", String(255), nullable=True)
1663
1664 clone_uri = Column(
1665 "clone_uri", EncryptedTextValue(), nullable=True, unique=False,
1666 default=None)
1667 push_uri = Column(
1668 "push_uri", EncryptedTextValue(), nullable=True, unique=False,
1669 default=None)
1670 repo_type = Column(
1671 "repo_type", String(255), nullable=False, unique=False, default=None)
1672 user_id = Column(
1673 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
1674 unique=False, default=None)
1675 private = Column(
1676 "private", Boolean(), nullable=True, unique=None, default=None)
1677 archived = Column(
1678 "archived", Boolean(), nullable=True, unique=None, default=None)
1679 enable_statistics = Column(
1680 "statistics", Boolean(), nullable=True, unique=None, default=True)
1681 enable_downloads = Column(
1682 "downloads", Boolean(), nullable=True, unique=None, default=True)
1683 description = Column(
1684 "description", String(10000), nullable=True, unique=None, default=None)
1685 created_on = Column(
1686 'created_on', DateTime(timezone=False), nullable=True, unique=None,
1687 default=datetime.datetime.now)
1688 updated_on = Column(
1689 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
1690 default=datetime.datetime.now)
1691 _landing_revision = Column(
1692 "landing_revision", String(255), nullable=False, unique=False,
1693 default=None)
1694 enable_locking = Column(
1695 "enable_locking", Boolean(), nullable=False, unique=None,
1696 default=False)
1697 _locked = Column(
1698 "locked", String(255), nullable=True, unique=False, default=None)
1699 _changeset_cache = Column(
1700 "changeset_cache", LargeBinary(), nullable=True) # JSON data
1701
1702 fork_id = Column(
1703 "fork_id", Integer(), ForeignKey('repositories.repo_id'),
1704 nullable=True, unique=False, default=None)
1705 group_id = Column(
1706 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1707 unique=False, default=None)
1708
1709 user = relationship('User', lazy='joined')
1710 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1711 group = relationship('RepoGroup', lazy='joined')
1712 repo_to_perm = relationship(
1713 'UserRepoToPerm', cascade='all',
1714 order_by='UserRepoToPerm.repo_to_perm_id')
1715 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1716 stats = relationship('Statistics', cascade='all', uselist=False)
1717
1718 followers = relationship(
1719 'UserFollowing',
1720 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
1721 cascade='all')
1722 extra_fields = relationship(
1723 'RepositoryField', cascade="all, delete-orphan")
1724 logs = relationship('UserLog')
1725 comments = relationship(
1726 'ChangesetComment', cascade="all, delete-orphan")
1727 pull_requests_source = relationship(
1728 'PullRequest',
1729 primaryjoin='PullRequest.source_repo_id==Repository.repo_id',
1730 cascade="all, delete-orphan")
1731 pull_requests_target = relationship(
1732 'PullRequest',
1733 primaryjoin='PullRequest.target_repo_id==Repository.repo_id',
1734 cascade="all, delete-orphan")
1735 ui = relationship('RepoRhodeCodeUi', cascade="all")
1736 settings = relationship('RepoRhodeCodeSetting', cascade="all")
1737 integrations = relationship('Integration', cascade="all, delete-orphan")
1738
1739 scoped_tokens = relationship('UserApiKeys', cascade="all")
1740
1741 # no cascade, set NULL
1742 artifacts = relationship('FileStore', primaryjoin='FileStore.scope_repo_id==Repository.repo_id')
1743
1744 def __unicode__(self):
1745 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
1746 safe_unicode(self.repo_name))
1747
1748 @hybrid_property
1749 def description_safe(self):
1750 from rhodecode.lib import helpers as h
1751 return h.escape(self.description)
1752
1753 @hybrid_property
1754 def landing_rev(self):
1755 # always should return [rev_type, rev]
1756 if self._landing_revision:
1757 _rev_info = self._landing_revision.split(':')
1758 if len(_rev_info) < 2:
1759 _rev_info.insert(0, 'rev')
1760 return [_rev_info[0], _rev_info[1]]
1761 return [None, None]
1762
1763 @landing_rev.setter
1764 def landing_rev(self, val):
1765 if ':' not in val:
1766 raise ValueError('value must be delimited with `:` and consist '
1767 'of <rev_type>:<rev>, got %s instead' % val)
1768 self._landing_revision = val
1769
1770 @hybrid_property
1771 def locked(self):
1772 if self._locked:
1773 user_id, timelocked, reason = self._locked.split(':')
1774 lock_values = int(user_id), timelocked, reason
1775 else:
1776 lock_values = [None, None, None]
1777 return lock_values
1778
1779 @locked.setter
1780 def locked(self, val):
1781 if val and isinstance(val, (list, tuple)):
1782 self._locked = ':'.join(map(str, val))
1783 else:
1784 self._locked = None
1785
1786 @classmethod
1787 def _load_changeset_cache(cls, repo_id, changeset_cache_raw):
1788 from rhodecode.lib.vcs.backends.base import EmptyCommit
1789 dummy = EmptyCommit().__json__()
1790 if not changeset_cache_raw:
1791 dummy['source_repo_id'] = repo_id
1792 return json.loads(json.dumps(dummy))
1793
1794 try:
1795 return json.loads(changeset_cache_raw)
1796 except TypeError:
1797 return dummy
1798 except Exception:
1799 log.error(traceback.format_exc())
1800 return dummy
1801
1802 @hybrid_property
1803 def changeset_cache(self):
1804 return self._load_changeset_cache(self.repo_id, self._changeset_cache)
1805
1806 @changeset_cache.setter
1807 def changeset_cache(self, val):
1808 try:
1809 self._changeset_cache = json.dumps(val)
1810 except Exception:
1811 log.error(traceback.format_exc())
1812
1813 @hybrid_property
1814 def repo_name(self):
1815 return self._repo_name
1816
1817 @repo_name.setter
1818 def repo_name(self, value):
1819 self._repo_name = value
1820 self.repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest()
1821
1822 @classmethod
1823 def normalize_repo_name(cls, repo_name):
1824 """
1825 Normalizes os specific repo_name to the format internally stored inside
1826 database using URL_SEP
1827
1828 :param cls:
1829 :param repo_name:
1830 """
1831 return cls.NAME_SEP.join(repo_name.split(os.sep))
1832
1833 @classmethod
1834 def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False):
1835 session = Session()
1836 q = session.query(cls).filter(cls.repo_name == repo_name)
1837
1838 if cache:
1839 if identity_cache:
1840 val = cls.identity_cache(session, 'repo_name', repo_name)
1841 if val:
1842 return val
1843 else:
1844 cache_key = "get_repo_by_name_%s" % _hash_key(repo_name)
1845 q = q.options(
1846 FromCache("sql_cache_short", cache_key))
1847
1848 return q.scalar()
1849
1850 @classmethod
1851 def get_by_id_or_repo_name(cls, repoid):
1852 if isinstance(repoid, (int, long)):
1853 try:
1854 repo = cls.get(repoid)
1855 except ValueError:
1856 repo = None
1857 else:
1858 repo = cls.get_by_repo_name(repoid)
1859 return repo
1860
1861 @classmethod
1862 def get_by_full_path(cls, repo_full_path):
1863 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
1864 repo_name = cls.normalize_repo_name(repo_name)
1865 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
1866
1867 @classmethod
1868 def get_repo_forks(cls, repo_id):
1869 return cls.query().filter(Repository.fork_id == repo_id)
1870
1871 @classmethod
1872 def base_path(cls):
1873 """
1874 Returns base path when all repos are stored
1875
1876 :param cls:
1877 """
1878 q = Session().query(RhodeCodeUi)\
1879 .filter(RhodeCodeUi.ui_key == cls.NAME_SEP)
1880 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1881 return q.one().ui_value
1882
1883 @classmethod
1884 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1885 case_insensitive=True, archived=False):
1886 q = Repository.query()
1887
1888 if not archived:
1889 q = q.filter(Repository.archived.isnot(true()))
1890
1891 if not isinstance(user_id, Optional):
1892 q = q.filter(Repository.user_id == user_id)
1893
1894 if not isinstance(group_id, Optional):
1895 q = q.filter(Repository.group_id == group_id)
1896
1897 if case_insensitive:
1898 q = q.order_by(func.lower(Repository.repo_name))
1899 else:
1900 q = q.order_by(Repository.repo_name)
1901
1902 return q.all()
1903
1904 @property
1905 def repo_uid(self):
1906 return '_{}'.format(self.repo_id)
1907
1908 @property
1909 def forks(self):
1910 """
1911 Return forks of this repo
1912 """
1913 return Repository.get_repo_forks(self.repo_id)
1914
1915 @property
1916 def parent(self):
1917 """
1918 Returns fork parent
1919 """
1920 return self.fork
1921
1922 @property
1923 def just_name(self):
1924 return self.repo_name.split(self.NAME_SEP)[-1]
1925
1926 @property
1927 def groups_with_parents(self):
1928 groups = []
1929 if self.group is None:
1930 return groups
1931
1932 cur_gr = self.group
1933 groups.insert(0, cur_gr)
1934 while 1:
1935 gr = getattr(cur_gr, 'parent_group', None)
1936 cur_gr = cur_gr.parent_group
1937 if gr is None:
1938 break
1939 groups.insert(0, gr)
1940
1941 return groups
1942
1943 @property
1944 def groups_and_repo(self):
1945 return self.groups_with_parents, self
1946
1947 @LazyProperty
1948 def repo_path(self):
1949 """
1950 Returns base full path for that repository means where it actually
1951 exists on a filesystem
1952 """
1953 q = Session().query(RhodeCodeUi).filter(
1954 RhodeCodeUi.ui_key == self.NAME_SEP)
1955 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1956 return q.one().ui_value
1957
1958 @property
1959 def repo_full_path(self):
1960 p = [self.repo_path]
1961 # we need to split the name by / since this is how we store the
1962 # names in the database, but that eventually needs to be converted
1963 # into a valid system path
1964 p += self.repo_name.split(self.NAME_SEP)
1965 return os.path.join(*map(safe_unicode, p))
1966
1967 @property
1968 def cache_keys(self):
1969 """
1970 Returns associated cache keys for that repo
1971 """
1972 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
1973 repo_id=self.repo_id)
1974 return CacheKey.query()\
1975 .filter(CacheKey.cache_args == invalidation_namespace)\
1976 .order_by(CacheKey.cache_key)\
1977 .all()
1978
1979 @property
1980 def cached_diffs_relative_dir(self):
1981 """
1982 Return a relative to the repository store path of cached diffs
1983 used for safe display for users, who shouldn't know the absolute store
1984 path
1985 """
1986 return os.path.join(
1987 os.path.dirname(self.repo_name),
1988 self.cached_diffs_dir.split(os.path.sep)[-1])
1989
1990 @property
1991 def cached_diffs_dir(self):
1992 path = self.repo_full_path
1993 return os.path.join(
1994 os.path.dirname(path),
1995 '.__shadow_diff_cache_repo_{}'.format(self.repo_id))
1996
1997 def cached_diffs(self):
1998 diff_cache_dir = self.cached_diffs_dir
1999 if os.path.isdir(diff_cache_dir):
2000 return os.listdir(diff_cache_dir)
2001 return []
2002
2003 def shadow_repos(self):
2004 shadow_repos_pattern = '.__shadow_repo_{}'.format(self.repo_id)
2005 return [
2006 x for x in os.listdir(os.path.dirname(self.repo_full_path))
2007 if x.startswith(shadow_repos_pattern)]
2008
2009 def get_new_name(self, repo_name):
2010 """
2011 returns new full repository name based on assigned group and new new
2012
2013 :param group_name:
2014 """
2015 path_prefix = self.group.full_path_splitted if self.group else []
2016 return self.NAME_SEP.join(path_prefix + [repo_name])
2017
2018 @property
2019 def _config(self):
2020 """
2021 Returns db based config object.
2022 """
2023 from rhodecode.lib.utils import make_db_config
2024 return make_db_config(clear_session=False, repo=self)
2025
2026 def permissions(self, with_admins=True, with_owner=True,
2027 expand_from_user_groups=False):
2028 """
2029 Permissions for repositories
2030 """
2031 _admin_perm = 'repository.admin'
2032
2033 owner_row = []
2034 if with_owner:
2035 usr = AttributeDict(self.user.get_dict())
2036 usr.owner_row = True
2037 usr.permission = _admin_perm
2038 usr.permission_id = None
2039 owner_row.append(usr)
2040
2041 super_admin_ids = []
2042 super_admin_rows = []
2043 if with_admins:
2044 for usr in User.get_all_super_admins():
2045 super_admin_ids.append(usr.user_id)
2046 # if this admin is also owner, don't double the record
2047 if usr.user_id == owner_row[0].user_id:
2048 owner_row[0].admin_row = True
2049 else:
2050 usr = AttributeDict(usr.get_dict())
2051 usr.admin_row = True
2052 usr.permission = _admin_perm
2053 usr.permission_id = None
2054 super_admin_rows.append(usr)
2055
2056 q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self)
2057 q = q.options(joinedload(UserRepoToPerm.repository),
2058 joinedload(UserRepoToPerm.user),
2059 joinedload(UserRepoToPerm.permission),)
2060
2061 # get owners and admins and permissions. We do a trick of re-writing
2062 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2063 # has a global reference and changing one object propagates to all
2064 # others. This means if admin is also an owner admin_row that change
2065 # would propagate to both objects
2066 perm_rows = []
2067 for _usr in q.all():
2068 usr = AttributeDict(_usr.user.get_dict())
2069 # if this user is also owner/admin, mark as duplicate record
2070 if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids:
2071 usr.duplicate_perm = True
2072 # also check if this permission is maybe used by branch_permissions
2073 if _usr.branch_perm_entry:
2074 usr.branch_rules = [x.branch_rule_id for x in _usr.branch_perm_entry]
2075
2076 usr.permission = _usr.permission.permission_name
2077 usr.permission_id = _usr.repo_to_perm_id
2078 perm_rows.append(usr)
2079
2080 # filter the perm rows by 'default' first and then sort them by
2081 # admin,write,read,none permissions sorted again alphabetically in
2082 # each group
2083 perm_rows = sorted(perm_rows, key=display_user_sort)
2084
2085 user_groups_rows = []
2086 if expand_from_user_groups:
2087 for ug in self.permission_user_groups(with_members=True):
2088 for user_data in ug.members:
2089 user_groups_rows.append(user_data)
2090
2091 return super_admin_rows + owner_row + perm_rows + user_groups_rows
2092
2093 def permission_user_groups(self, with_members=True):
2094 q = UserGroupRepoToPerm.query()\
2095 .filter(UserGroupRepoToPerm.repository == self)
2096 q = q.options(joinedload(UserGroupRepoToPerm.repository),
2097 joinedload(UserGroupRepoToPerm.users_group),
2098 joinedload(UserGroupRepoToPerm.permission),)
2099
2100 perm_rows = []
2101 for _user_group in q.all():
2102 entry = AttributeDict(_user_group.users_group.get_dict())
2103 entry.permission = _user_group.permission.permission_name
2104 if with_members:
2105 entry.members = [x.user.get_dict()
2106 for x in _user_group.users_group.members]
2107 perm_rows.append(entry)
2108
2109 perm_rows = sorted(perm_rows, key=display_user_group_sort)
2110 return perm_rows
2111
2112 def get_api_data(self, include_secrets=False):
2113 """
2114 Common function for generating repo api data
2115
2116 :param include_secrets: See :meth:`User.get_api_data`.
2117
2118 """
2119 # TODO: mikhail: Here there is an anti-pattern, we probably need to
2120 # move this methods on models level.
2121 from rhodecode.model.settings import SettingsModel
2122 from rhodecode.model.repo import RepoModel
2123
2124 repo = self
2125 _user_id, _time, _reason = self.locked
2126
2127 data = {
2128 'repo_id': repo.repo_id,
2129 'repo_name': repo.repo_name,
2130 'repo_type': repo.repo_type,
2131 'clone_uri': repo.clone_uri or '',
2132 'push_uri': repo.push_uri or '',
2133 'url': RepoModel().get_url(self),
2134 'private': repo.private,
2135 'created_on': repo.created_on,
2136 'description': repo.description_safe,
2137 'landing_rev': repo.landing_rev,
2138 'owner': repo.user.username,
2139 'fork_of': repo.fork.repo_name if repo.fork else None,
2140 'fork_of_id': repo.fork.repo_id if repo.fork else None,
2141 'enable_statistics': repo.enable_statistics,
2142 'enable_locking': repo.enable_locking,
2143 'enable_downloads': repo.enable_downloads,
2144 'last_changeset': repo.changeset_cache,
2145 'locked_by': User.get(_user_id).get_api_data(
2146 include_secrets=include_secrets) if _user_id else None,
2147 'locked_date': time_to_datetime(_time) if _time else None,
2148 'lock_reason': _reason if _reason else None,
2149 }
2150
2151 # TODO: mikhail: should be per-repo settings here
2152 rc_config = SettingsModel().get_all_settings()
2153 repository_fields = str2bool(
2154 rc_config.get('rhodecode_repository_fields'))
2155 if repository_fields:
2156 for f in self.extra_fields:
2157 data[f.field_key_prefixed] = f.field_value
2158
2159 return data
2160
2161 @classmethod
2162 def lock(cls, repo, user_id, lock_time=None, lock_reason=None):
2163 if not lock_time:
2164 lock_time = time.time()
2165 if not lock_reason:
2166 lock_reason = cls.LOCK_AUTOMATIC
2167 repo.locked = [user_id, lock_time, lock_reason]
2168 Session().add(repo)
2169 Session().commit()
2170
2171 @classmethod
2172 def unlock(cls, repo):
2173 repo.locked = None
2174 Session().add(repo)
2175 Session().commit()
2176
2177 @classmethod
2178 def getlock(cls, repo):
2179 return repo.locked
2180
2181 def is_user_lock(self, user_id):
2182 if self.lock[0]:
2183 lock_user_id = safe_int(self.lock[0])
2184 user_id = safe_int(user_id)
2185 # both are ints, and they are equal
2186 return all([lock_user_id, user_id]) and lock_user_id == user_id
2187
2188 return False
2189
2190 def get_locking_state(self, action, user_id, only_when_enabled=True):
2191 """
2192 Checks locking on this repository, if locking is enabled and lock is
2193 present returns a tuple of make_lock, locked, locked_by.
2194 make_lock can have 3 states None (do nothing) True, make lock
2195 False release lock, This value is later propagated to hooks, which
2196 do the locking. Think about this as signals passed to hooks what to do.
2197
2198 """
2199 # TODO: johbo: This is part of the business logic and should be moved
2200 # into the RepositoryModel.
2201
2202 if action not in ('push', 'pull'):
2203 raise ValueError("Invalid action value: %s" % repr(action))
2204
2205 # defines if locked error should be thrown to user
2206 currently_locked = False
2207 # defines if new lock should be made, tri-state
2208 make_lock = None
2209 repo = self
2210 user = User.get(user_id)
2211
2212 lock_info = repo.locked
2213
2214 if repo and (repo.enable_locking or not only_when_enabled):
2215 if action == 'push':
2216 # check if it's already locked !, if it is compare users
2217 locked_by_user_id = lock_info[0]
2218 if user.user_id == locked_by_user_id:
2219 log.debug(
2220 'Got `push` action from user %s, now unlocking', user)
2221 # unlock if we have push from user who locked
2222 make_lock = False
2223 else:
2224 # we're not the same user who locked, ban with
2225 # code defined in settings (default is 423 HTTP Locked) !
2226 log.debug('Repo %s is currently locked by %s', repo, user)
2227 currently_locked = True
2228 elif action == 'pull':
2229 # [0] user [1] date
2230 if lock_info[0] and lock_info[1]:
2231 log.debug('Repo %s is currently locked by %s', repo, user)
2232 currently_locked = True
2233 else:
2234 log.debug('Setting lock on repo %s by %s', repo, user)
2235 make_lock = True
2236
2237 else:
2238 log.debug('Repository %s do not have locking enabled', repo)
2239
2240 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s',
2241 make_lock, currently_locked, lock_info)
2242
2243 from rhodecode.lib.auth import HasRepoPermissionAny
2244 perm_check = HasRepoPermissionAny('repository.write', 'repository.admin')
2245 if make_lock and not perm_check(repo_name=repo.repo_name, user=user):
2246 # if we don't have at least write permission we cannot make a lock
2247 log.debug('lock state reset back to FALSE due to lack '
2248 'of at least read permission')
2249 make_lock = False
2250
2251 return make_lock, currently_locked, lock_info
2252
2253 @property
2254 def last_commit_cache_update_diff(self):
2255 return time.time() - (safe_int(self.changeset_cache.get('updated_on')) or 0)
2256
2257 @classmethod
2258 def _load_commit_change(cls, last_commit_cache):
2259 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2260 empty_date = datetime.datetime.fromtimestamp(0)
2261 date_latest = last_commit_cache.get('date', empty_date)
2262 try:
2263 return parse_datetime(date_latest)
2264 except Exception:
2265 return empty_date
2266
2267 @property
2268 def last_commit_change(self):
2269 return self._load_commit_change(self.changeset_cache)
2270
2271 @property
2272 def last_db_change(self):
2273 return self.updated_on
2274
2275 @property
2276 def clone_uri_hidden(self):
2277 clone_uri = self.clone_uri
2278 if clone_uri:
2279 import urlobject
2280 url_obj = urlobject.URLObject(cleaned_uri(clone_uri))
2281 if url_obj.password:
2282 clone_uri = url_obj.with_password('*****')
2283 return clone_uri
2284
2285 @property
2286 def push_uri_hidden(self):
2287 push_uri = self.push_uri
2288 if push_uri:
2289 import urlobject
2290 url_obj = urlobject.URLObject(cleaned_uri(push_uri))
2291 if url_obj.password:
2292 push_uri = url_obj.with_password('*****')
2293 return push_uri
2294
2295 def clone_url(self, **override):
2296 from rhodecode.model.settings import SettingsModel
2297
2298 uri_tmpl = None
2299 if 'with_id' in override:
2300 uri_tmpl = self.DEFAULT_CLONE_URI_ID
2301 del override['with_id']
2302
2303 if 'uri_tmpl' in override:
2304 uri_tmpl = override['uri_tmpl']
2305 del override['uri_tmpl']
2306
2307 ssh = False
2308 if 'ssh' in override:
2309 ssh = True
2310 del override['ssh']
2311
2312 # we didn't override our tmpl from **overrides
2313 request = get_current_request()
2314 if not uri_tmpl:
2315 if hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
2316 rc_config = request.call_context.rc_config
2317 else:
2318 rc_config = SettingsModel().get_all_settings(cache=True)
2319
2320 if ssh:
2321 uri_tmpl = rc_config.get(
2322 'rhodecode_clone_uri_ssh_tmpl') or self.DEFAULT_CLONE_URI_SSH
2323
2324 else:
2325 uri_tmpl = rc_config.get(
2326 'rhodecode_clone_uri_tmpl') or self.DEFAULT_CLONE_URI
2327
2328 return get_clone_url(request=request,
2329 uri_tmpl=uri_tmpl,
2330 repo_name=self.repo_name,
2331 repo_id=self.repo_id,
2332 repo_type=self.repo_type,
2333 **override)
2334
2335 def set_state(self, state):
2336 self.repo_state = state
2337 Session().add(self)
2338 #==========================================================================
2339 # SCM PROPERTIES
2340 #==========================================================================
2341
2342 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, maybe_unreachable=False):
2343 return get_commit_safe(
2344 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load,
2345 maybe_unreachable=maybe_unreachable)
2346
2347 def get_changeset(self, rev=None, pre_load=None):
2348 warnings.warn("Use get_commit", DeprecationWarning)
2349 commit_id = None
2350 commit_idx = None
2351 if isinstance(rev, compat.string_types):
2352 commit_id = rev
2353 else:
2354 commit_idx = rev
2355 return self.get_commit(commit_id=commit_id, commit_idx=commit_idx,
2356 pre_load=pre_load)
2357
2358 def get_landing_commit(self):
2359 """
2360 Returns landing commit, or if that doesn't exist returns the tip
2361 """
2362 _rev_type, _rev = self.landing_rev
2363 commit = self.get_commit(_rev)
2364 if isinstance(commit, EmptyCommit):
2365 return self.get_commit()
2366 return commit
2367
2368 def flush_commit_cache(self):
2369 self.update_commit_cache(cs_cache={'raw_id':'0'})
2370 self.update_commit_cache()
2371
2372 def update_commit_cache(self, cs_cache=None, config=None):
2373 """
2374 Update cache of last commit for repository
2375 cache_keys should be::
2376
2377 source_repo_id
2378 short_id
2379 raw_id
2380 revision
2381 parents
2382 message
2383 date
2384 author
2385 updated_on
2386
2387 """
2388 from rhodecode.lib.vcs.backends.base import BaseChangeset
2389 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2390 empty_date = datetime.datetime.fromtimestamp(0)
2391
2392 if cs_cache is None:
2393 # use no-cache version here
2394 try:
2395 scm_repo = self.scm_instance(cache=False, config=config)
2396 except VCSError:
2397 scm_repo = None
2398 empty = scm_repo is None or scm_repo.is_empty()
2399
2400 if not empty:
2401 cs_cache = scm_repo.get_commit(
2402 pre_load=["author", "date", "message", "parents", "branch"])
2403 else:
2404 cs_cache = EmptyCommit()
2405
2406 if isinstance(cs_cache, BaseChangeset):
2407 cs_cache = cs_cache.__json__()
2408
2409 def is_outdated(new_cs_cache):
2410 if (new_cs_cache['raw_id'] != self.changeset_cache['raw_id'] or
2411 new_cs_cache['revision'] != self.changeset_cache['revision']):
2412 return True
2413 return False
2414
2415 # check if we have maybe already latest cached revision
2416 if is_outdated(cs_cache) or not self.changeset_cache:
2417 _current_datetime = datetime.datetime.utcnow()
2418 last_change = cs_cache.get('date') or _current_datetime
2419 # we check if last update is newer than the new value
2420 # if yes, we use the current timestamp instead. Imagine you get
2421 # old commit pushed 1y ago, we'd set last update 1y to ago.
2422 last_change_timestamp = datetime_to_time(last_change)
2423 current_timestamp = datetime_to_time(last_change)
2424 if last_change_timestamp > current_timestamp and not empty:
2425 cs_cache['date'] = _current_datetime
2426
2427 _date_latest = parse_datetime(cs_cache.get('date') or empty_date)
2428 cs_cache['updated_on'] = time.time()
2429 self.changeset_cache = cs_cache
2430 self.updated_on = last_change
2431 Session().add(self)
2432 Session().commit()
2433
2434 else:
2435 if empty:
2436 cs_cache = EmptyCommit().__json__()
2437 else:
2438 cs_cache = self.changeset_cache
2439
2440 _date_latest = parse_datetime(cs_cache.get('date') or empty_date)
2441
2442 cs_cache['updated_on'] = time.time()
2443 self.changeset_cache = cs_cache
2444 self.updated_on = _date_latest
2445 Session().add(self)
2446 Session().commit()
2447
2448 log.debug('updated repo `%s` with new commit cache %s, and last update_date: %s',
2449 self.repo_name, cs_cache, _date_latest)
2450
2451 @property
2452 def tip(self):
2453 return self.get_commit('tip')
2454
2455 @property
2456 def author(self):
2457 return self.tip.author
2458
2459 @property
2460 def last_change(self):
2461 return self.scm_instance().last_change
2462
2463 def get_comments(self, revisions=None):
2464 """
2465 Returns comments for this repository grouped by revisions
2466
2467 :param revisions: filter query by revisions only
2468 """
2469 cmts = ChangesetComment.query()\
2470 .filter(ChangesetComment.repo == self)
2471 if revisions:
2472 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
2473 grouped = collections.defaultdict(list)
2474 for cmt in cmts.all():
2475 grouped[cmt.revision].append(cmt)
2476 return grouped
2477
2478 def statuses(self, revisions=None):
2479 """
2480 Returns statuses for this repository
2481
2482 :param revisions: list of revisions to get statuses for
2483 """
2484 statuses = ChangesetStatus.query()\
2485 .filter(ChangesetStatus.repo == self)\
2486 .filter(ChangesetStatus.version == 0)
2487
2488 if revisions:
2489 # Try doing the filtering in chunks to avoid hitting limits
2490 size = 500
2491 status_results = []
2492 for chunk in xrange(0, len(revisions), size):
2493 status_results += statuses.filter(
2494 ChangesetStatus.revision.in_(
2495 revisions[chunk: chunk+size])
2496 ).all()
2497 else:
2498 status_results = statuses.all()
2499
2500 grouped = {}
2501
2502 # maybe we have open new pullrequest without a status?
2503 stat = ChangesetStatus.STATUS_UNDER_REVIEW
2504 status_lbl = ChangesetStatus.get_status_lbl(stat)
2505 for pr in PullRequest.query().filter(PullRequest.source_repo == self).all():
2506 for rev in pr.revisions:
2507 pr_id = pr.pull_request_id
2508 pr_repo = pr.target_repo.repo_name
2509 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
2510
2511 for stat in status_results:
2512 pr_id = pr_repo = None
2513 if stat.pull_request:
2514 pr_id = stat.pull_request.pull_request_id
2515 pr_repo = stat.pull_request.target_repo.repo_name
2516 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
2517 pr_id, pr_repo]
2518 return grouped
2519
2520 # ==========================================================================
2521 # SCM CACHE INSTANCE
2522 # ==========================================================================
2523
2524 def scm_instance(self, **kwargs):
2525 import rhodecode
2526
2527 # Passing a config will not hit the cache currently only used
2528 # for repo2dbmapper
2529 config = kwargs.pop('config', None)
2530 cache = kwargs.pop('cache', None)
2531 vcs_full_cache = kwargs.pop('vcs_full_cache', None)
2532 if vcs_full_cache is not None:
2533 # allows override global config
2534 full_cache = vcs_full_cache
2535 else:
2536 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
2537 # if cache is NOT defined use default global, else we have a full
2538 # control over cache behaviour
2539 if cache is None and full_cache and not config:
2540 log.debug('Initializing pure cached instance for %s', self.repo_path)
2541 return self._get_instance_cached()
2542
2543 # cache here is sent to the "vcs server"
2544 return self._get_instance(cache=bool(cache), config=config)
2545
2546 def _get_instance_cached(self):
2547 from rhodecode.lib import rc_cache
2548
2549 cache_namespace_uid = 'cache_repo_instance.{}'.format(self.repo_id)
2550 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
2551 repo_id=self.repo_id)
2552 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
2553
2554 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
2555 def get_instance_cached(repo_id, context_id, _cache_state_uid):
2556 return self._get_instance(repo_state_uid=_cache_state_uid)
2557
2558 # we must use thread scoped cache here,
2559 # because each thread of gevent needs it's own not shared connection and cache
2560 # we also alter `args` so the cache key is individual for every green thread.
2561 inv_context_manager = rc_cache.InvalidationContext(
2562 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace,
2563 thread_scoped=True)
2564 with inv_context_manager as invalidation_context:
2565 cache_state_uid = invalidation_context.cache_data['cache_state_uid']
2566 args = (self.repo_id, inv_context_manager.cache_key, cache_state_uid)
2567
2568 # re-compute and store cache if we get invalidate signal
2569 if invalidation_context.should_invalidate():
2570 instance = get_instance_cached.refresh(*args)
2571 else:
2572 instance = get_instance_cached(*args)
2573
2574 log.debug('Repo instance fetched in %.4fs', inv_context_manager.compute_time)
2575 return instance
2576
2577 def _get_instance(self, cache=True, config=None, repo_state_uid=None):
2578 log.debug('Initializing %s instance `%s` with cache flag set to: %s',
2579 self.repo_type, self.repo_path, cache)
2580 config = config or self._config
2581 custom_wire = {
2582 'cache': cache, # controls the vcs.remote cache
2583 'repo_state_uid': repo_state_uid
2584 }
2585 repo = get_vcs_instance(
2586 repo_path=safe_str(self.repo_full_path),
2587 config=config,
2588 with_wire=custom_wire,
2589 create=False,
2590 _vcs_alias=self.repo_type)
2591 if repo is not None:
2592 repo.count() # cache rebuild
2593 return repo
2594
2595 def get_shadow_repository_path(self, workspace_id):
2596 from rhodecode.lib.vcs.backends.base import BaseRepository
2597 shadow_repo_path = BaseRepository._get_shadow_repository_path(
2598 self.repo_full_path, self.repo_id, workspace_id)
2599 return shadow_repo_path
2600
2601 def __json__(self):
2602 return {'landing_rev': self.landing_rev}
2603
2604 def get_dict(self):
2605
2606 # Since we transformed `repo_name` to a hybrid property, we need to
2607 # keep compatibility with the code which uses `repo_name` field.
2608
2609 result = super(Repository, self).get_dict()
2610 result['repo_name'] = result.pop('_repo_name', None)
2611 return result
2612
2613
2614 class RepoGroup(Base, BaseModel):
2615 __tablename__ = 'groups'
2616 __table_args__ = (
2617 UniqueConstraint('group_name', 'group_parent_id'),
2618 base_table_args,
2619 )
2620 __mapper_args__ = {'order_by': 'group_name'}
2621
2622 CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
2623
2624 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2625 _group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
2626 group_name_hash = Column("repo_group_name_hash", String(1024), nullable=False, unique=False)
2627 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
2628 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
2629 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
2630 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
2631 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2632 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
2633 personal = Column('personal', Boolean(), nullable=True, unique=None, default=None)
2634 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) # JSON data
2635
2636 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
2637 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
2638 parent_group = relationship('RepoGroup', remote_side=group_id)
2639 user = relationship('User')
2640 integrations = relationship('Integration', cascade="all, delete-orphan")
2641
2642 # no cascade, set NULL
2643 scope_artifacts = relationship('FileStore', primaryjoin='FileStore.scope_repo_group_id==RepoGroup.group_id')
2644
2645 def __init__(self, group_name='', parent_group=None):
2646 self.group_name = group_name
2647 self.parent_group = parent_group
2648
2649 def __unicode__(self):
2650 return u"<%s('id:%s:%s')>" % (
2651 self.__class__.__name__, self.group_id, self.group_name)
2652
2653 @hybrid_property
2654 def group_name(self):
2655 return self._group_name
2656
2657 @group_name.setter
2658 def group_name(self, value):
2659 self._group_name = value
2660 self.group_name_hash = self.hash_repo_group_name(value)
2661
2662 @classmethod
2663 def _load_changeset_cache(cls, repo_id, changeset_cache_raw):
2664 from rhodecode.lib.vcs.backends.base import EmptyCommit
2665 dummy = EmptyCommit().__json__()
2666 if not changeset_cache_raw:
2667 dummy['source_repo_id'] = repo_id
2668 return json.loads(json.dumps(dummy))
2669
2670 try:
2671 return json.loads(changeset_cache_raw)
2672 except TypeError:
2673 return dummy
2674 except Exception:
2675 log.error(traceback.format_exc())
2676 return dummy
2677
2678 @hybrid_property
2679 def changeset_cache(self):
2680 return self._load_changeset_cache('', self._changeset_cache)
2681
2682 @changeset_cache.setter
2683 def changeset_cache(self, val):
2684 try:
2685 self._changeset_cache = json.dumps(val)
2686 except Exception:
2687 log.error(traceback.format_exc())
2688
2689 @validates('group_parent_id')
2690 def validate_group_parent_id(self, key, val):
2691 """
2692 Check cycle references for a parent group to self
2693 """
2694 if self.group_id and val:
2695 assert val != self.group_id
2696
2697 return val
2698
2699 @hybrid_property
2700 def description_safe(self):
2701 from rhodecode.lib import helpers as h
2702 return h.escape(self.group_description)
2703
2704 @classmethod
2705 def hash_repo_group_name(cls, repo_group_name):
2706 val = remove_formatting(repo_group_name)
2707 val = safe_str(val).lower()
2708 chars = []
2709 for c in val:
2710 if c not in string.ascii_letters:
2711 c = str(ord(c))
2712 chars.append(c)
2713
2714 return ''.join(chars)
2715
2716 @classmethod
2717 def _generate_choice(cls, repo_group):
2718 from webhelpers2.html import literal as _literal
2719 _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k))
2720 return repo_group.group_id, _name(repo_group.full_path_splitted)
2721
2722 @classmethod
2723 def groups_choices(cls, groups=None, show_empty_group=True):
2724 if not groups:
2725 groups = cls.query().all()
2726
2727 repo_groups = []
2728 if show_empty_group:
2729 repo_groups = [(-1, u'-- %s --' % _('No parent'))]
2730
2731 repo_groups.extend([cls._generate_choice(x) for x in groups])
2732
2733 repo_groups = sorted(
2734 repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0])
2735 return repo_groups
2736
2737 @classmethod
2738 def url_sep(cls):
2739 return URL_SEP
2740
2741 @classmethod
2742 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
2743 if case_insensitive:
2744 gr = cls.query().filter(func.lower(cls.group_name)
2745 == func.lower(group_name))
2746 else:
2747 gr = cls.query().filter(cls.group_name == group_name)
2748 if cache:
2749 name_key = _hash_key(group_name)
2750 gr = gr.options(
2751 FromCache("sql_cache_short", "get_group_%s" % name_key))
2752 return gr.scalar()
2753
2754 @classmethod
2755 def get_user_personal_repo_group(cls, user_id):
2756 user = User.get(user_id)
2757 if user.username == User.DEFAULT_USER:
2758 return None
2759
2760 return cls.query()\
2761 .filter(cls.personal == true()) \
2762 .filter(cls.user == user) \
2763 .order_by(cls.group_id.asc()) \
2764 .first()
2765
2766 @classmethod
2767 def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None),
2768 case_insensitive=True):
2769 q = RepoGroup.query()
2770
2771 if not isinstance(user_id, Optional):
2772 q = q.filter(RepoGroup.user_id == user_id)
2773
2774 if not isinstance(group_id, Optional):
2775 q = q.filter(RepoGroup.group_parent_id == group_id)
2776
2777 if case_insensitive:
2778 q = q.order_by(func.lower(RepoGroup.group_name))
2779 else:
2780 q = q.order_by(RepoGroup.group_name)
2781 return q.all()
2782
2783 @property
2784 def parents(self, parents_recursion_limit=10):
2785 groups = []
2786 if self.parent_group is None:
2787 return groups
2788 cur_gr = self.parent_group
2789 groups.insert(0, cur_gr)
2790 cnt = 0
2791 while 1:
2792 cnt += 1
2793 gr = getattr(cur_gr, 'parent_group', None)
2794 cur_gr = cur_gr.parent_group
2795 if gr is None:
2796 break
2797 if cnt == parents_recursion_limit:
2798 # this will prevent accidental infinit loops
2799 log.error('more than %s parents found for group %s, stopping '
2800 'recursive parent fetching', parents_recursion_limit, self)
2801 break
2802
2803 groups.insert(0, gr)
2804 return groups
2805
2806 @property
2807 def last_commit_cache_update_diff(self):
2808 return time.time() - (safe_int(self.changeset_cache.get('updated_on')) or 0)
2809
2810 @classmethod
2811 def _load_commit_change(cls, last_commit_cache):
2812 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2813 empty_date = datetime.datetime.fromtimestamp(0)
2814 date_latest = last_commit_cache.get('date', empty_date)
2815 try:
2816 return parse_datetime(date_latest)
2817 except Exception:
2818 return empty_date
2819
2820 @property
2821 def last_commit_change(self):
2822 return self._load_commit_change(self.changeset_cache)
2823
2824 @property
2825 def last_db_change(self):
2826 return self.updated_on
2827
2828 @property
2829 def children(self):
2830 return RepoGroup.query().filter(RepoGroup.parent_group == self)
2831
2832 @property
2833 def name(self):
2834 return self.group_name.split(RepoGroup.url_sep())[-1]
2835
2836 @property
2837 def full_path(self):
2838 return self.group_name
2839
2840 @property
2841 def full_path_splitted(self):
2842 return self.group_name.split(RepoGroup.url_sep())
2843
2844 @property
2845 def repositories(self):
2846 return Repository.query()\
2847 .filter(Repository.group == self)\
2848 .order_by(Repository.repo_name)
2849
2850 @property
2851 def repositories_recursive_count(self):
2852 cnt = self.repositories.count()
2853
2854 def children_count(group):
2855 cnt = 0
2856 for child in group.children:
2857 cnt += child.repositories.count()
2858 cnt += children_count(child)
2859 return cnt
2860
2861 return cnt + children_count(self)
2862
2863 def _recursive_objects(self, include_repos=True, include_groups=True):
2864 all_ = []
2865
2866 def _get_members(root_gr):
2867 if include_repos:
2868 for r in root_gr.repositories:
2869 all_.append(r)
2870 childs = root_gr.children.all()
2871 if childs:
2872 for gr in childs:
2873 if include_groups:
2874 all_.append(gr)
2875 _get_members(gr)
2876
2877 root_group = []
2878 if include_groups:
2879 root_group = [self]
2880
2881 _get_members(self)
2882 return root_group + all_
2883
2884 def recursive_groups_and_repos(self):
2885 """
2886 Recursive return all groups, with repositories in those groups
2887 """
2888 return self._recursive_objects()
2889
2890 def recursive_groups(self):
2891 """
2892 Returns all children groups for this group including children of children
2893 """
2894 return self._recursive_objects(include_repos=False)
2895
2896 def recursive_repos(self):
2897 """
2898 Returns all children repositories for this group
2899 """
2900 return self._recursive_objects(include_groups=False)
2901
2902 def get_new_name(self, group_name):
2903 """
2904 returns new full group name based on parent and new name
2905
2906 :param group_name:
2907 """
2908 path_prefix = (self.parent_group.full_path_splitted if
2909 self.parent_group else [])
2910 return RepoGroup.url_sep().join(path_prefix + [group_name])
2911
2912 def update_commit_cache(self, config=None):
2913 """
2914 Update cache of last commit for newest repository inside this repository group.
2915 cache_keys should be::
2916
2917 source_repo_id
2918 short_id
2919 raw_id
2920 revision
2921 parents
2922 message
2923 date
2924 author
2925
2926 """
2927 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2928 empty_date = datetime.datetime.fromtimestamp(0)
2929
2930 def repo_groups_and_repos(root_gr):
2931 for _repo in root_gr.repositories:
2932 yield _repo
2933 for child_group in root_gr.children.all():
2934 yield child_group
2935
2936 latest_repo_cs_cache = {}
2937 for obj in repo_groups_and_repos(self):
2938 repo_cs_cache = obj.changeset_cache
2939 date_latest = latest_repo_cs_cache.get('date', empty_date)
2940 date_current = repo_cs_cache.get('date', empty_date)
2941 current_timestamp = datetime_to_time(parse_datetime(date_latest))
2942 if current_timestamp < datetime_to_time(parse_datetime(date_current)):
2943 latest_repo_cs_cache = repo_cs_cache
2944 if hasattr(obj, 'repo_id'):
2945 latest_repo_cs_cache['source_repo_id'] = obj.repo_id
2946 else:
2947 latest_repo_cs_cache['source_repo_id'] = repo_cs_cache.get('source_repo_id')
2948
2949 _date_latest = parse_datetime(latest_repo_cs_cache.get('date') or empty_date)
2950
2951 latest_repo_cs_cache['updated_on'] = time.time()
2952 self.changeset_cache = latest_repo_cs_cache
2953 self.updated_on = _date_latest
2954 Session().add(self)
2955 Session().commit()
2956
2957 log.debug('updated repo group `%s` with new commit cache %s, and last update_date: %s',
2958 self.group_name, latest_repo_cs_cache, _date_latest)
2959
2960 def permissions(self, with_admins=True, with_owner=True,
2961 expand_from_user_groups=False):
2962 """
2963 Permissions for repository groups
2964 """
2965 _admin_perm = 'group.admin'
2966
2967 owner_row = []
2968 if with_owner:
2969 usr = AttributeDict(self.user.get_dict())
2970 usr.owner_row = True
2971 usr.permission = _admin_perm
2972 owner_row.append(usr)
2973
2974 super_admin_ids = []
2975 super_admin_rows = []
2976 if with_admins:
2977 for usr in User.get_all_super_admins():
2978 super_admin_ids.append(usr.user_id)
2979 # if this admin is also owner, don't double the record
2980 if usr.user_id == owner_row[0].user_id:
2981 owner_row[0].admin_row = True
2982 else:
2983 usr = AttributeDict(usr.get_dict())
2984 usr.admin_row = True
2985 usr.permission = _admin_perm
2986 super_admin_rows.append(usr)
2987
2988 q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self)
2989 q = q.options(joinedload(UserRepoGroupToPerm.group),
2990 joinedload(UserRepoGroupToPerm.user),
2991 joinedload(UserRepoGroupToPerm.permission),)
2992
2993 # get owners and admins and permissions. We do a trick of re-writing
2994 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2995 # has a global reference and changing one object propagates to all
2996 # others. This means if admin is also an owner admin_row that change
2997 # would propagate to both objects
2998 perm_rows = []
2999 for _usr in q.all():
3000 usr = AttributeDict(_usr.user.get_dict())
3001 # if this user is also owner/admin, mark as duplicate record
3002 if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids:
3003 usr.duplicate_perm = True
3004 usr.permission = _usr.permission.permission_name
3005 perm_rows.append(usr)
3006
3007 # filter the perm rows by 'default' first and then sort them by
3008 # admin,write,read,none permissions sorted again alphabetically in
3009 # each group
3010 perm_rows = sorted(perm_rows, key=display_user_sort)
3011
3012 user_groups_rows = []
3013 if expand_from_user_groups:
3014 for ug in self.permission_user_groups(with_members=True):
3015 for user_data in ug.members:
3016 user_groups_rows.append(user_data)
3017
3018 return super_admin_rows + owner_row + perm_rows + user_groups_rows
3019
3020 def permission_user_groups(self, with_members=False):
3021 q = UserGroupRepoGroupToPerm.query()\
3022 .filter(UserGroupRepoGroupToPerm.group == self)
3023 q = q.options(joinedload(UserGroupRepoGroupToPerm.group),
3024 joinedload(UserGroupRepoGroupToPerm.users_group),
3025 joinedload(UserGroupRepoGroupToPerm.permission),)
3026
3027 perm_rows = []
3028 for _user_group in q.all():
3029 entry = AttributeDict(_user_group.users_group.get_dict())
3030 entry.permission = _user_group.permission.permission_name
3031 if with_members:
3032 entry.members = [x.user.get_dict()
3033 for x in _user_group.users_group.members]
3034 perm_rows.append(entry)
3035
3036 perm_rows = sorted(perm_rows, key=display_user_group_sort)
3037 return perm_rows
3038
3039 def get_api_data(self):
3040 """
3041 Common function for generating api data
3042
3043 """
3044 group = self
3045 data = {
3046 'group_id': group.group_id,
3047 'group_name': group.group_name,
3048 'group_description': group.description_safe,
3049 'parent_group': group.parent_group.group_name if group.parent_group else None,
3050 'repositories': [x.repo_name for x in group.repositories],
3051 'owner': group.user.username,
3052 }
3053 return data
3054
3055 def get_dict(self):
3056 # Since we transformed `group_name` to a hybrid property, we need to
3057 # keep compatibility with the code which uses `group_name` field.
3058 result = super(RepoGroup, self).get_dict()
3059 result['group_name'] = result.pop('_group_name', None)
3060 return result
3061
3062
3063 class Permission(Base, BaseModel):
3064 __tablename__ = 'permissions'
3065 __table_args__ = (
3066 Index('p_perm_name_idx', 'permission_name'),
3067 base_table_args,
3068 )
3069
3070 PERMS = [
3071 ('hg.admin', _('RhodeCode Super Administrator')),
3072
3073 ('repository.none', _('Repository no access')),
3074 ('repository.read', _('Repository read access')),
3075 ('repository.write', _('Repository write access')),
3076 ('repository.admin', _('Repository admin access')),
3077
3078 ('group.none', _('Repository group no access')),
3079 ('group.read', _('Repository group read access')),
3080 ('group.write', _('Repository group write access')),
3081 ('group.admin', _('Repository group admin access')),
3082
3083 ('usergroup.none', _('User group no access')),
3084 ('usergroup.read', _('User group read access')),
3085 ('usergroup.write', _('User group write access')),
3086 ('usergroup.admin', _('User group admin access')),
3087
3088 ('branch.none', _('Branch no permissions')),
3089 ('branch.merge', _('Branch access by web merge')),
3090 ('branch.push', _('Branch access by push')),
3091 ('branch.push_force', _('Branch access by push with force')),
3092
3093 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
3094 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
3095
3096 ('hg.usergroup.create.false', _('User Group creation disabled')),
3097 ('hg.usergroup.create.true', _('User Group creation enabled')),
3098
3099 ('hg.create.none', _('Repository creation disabled')),
3100 ('hg.create.repository', _('Repository creation enabled')),
3101 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
3102 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
3103
3104 ('hg.fork.none', _('Repository forking disabled')),
3105 ('hg.fork.repository', _('Repository forking enabled')),
3106
3107 ('hg.register.none', _('Registration disabled')),
3108 ('hg.register.manual_activate', _('User Registration with manual account activation')),
3109 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
3110
3111 ('hg.password_reset.enabled', _('Password reset enabled')),
3112 ('hg.password_reset.hidden', _('Password reset hidden')),
3113 ('hg.password_reset.disabled', _('Password reset disabled')),
3114
3115 ('hg.extern_activate.manual', _('Manual activation of external account')),
3116 ('hg.extern_activate.auto', _('Automatic activation of external account')),
3117
3118 ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')),
3119 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
3120 ]
3121
3122 # definition of system default permissions for DEFAULT user, created on
3123 # system setup
3124 DEFAULT_USER_PERMISSIONS = [
3125 # object perms
3126 'repository.read',
3127 'group.read',
3128 'usergroup.read',
3129 # branch, for backward compat we need same value as before so forced pushed
3130 'branch.push_force',
3131 # global
3132 'hg.create.repository',
3133 'hg.repogroup.create.false',
3134 'hg.usergroup.create.false',
3135 'hg.create.write_on_repogroup.true',
3136 'hg.fork.repository',
3137 'hg.register.manual_activate',
3138 'hg.password_reset.enabled',
3139 'hg.extern_activate.auto',
3140 'hg.inherit_default_perms.true',
3141 ]
3142
3143 # defines which permissions are more important higher the more important
3144 # Weight defines which permissions are more important.
3145 # The higher number the more important.
3146 PERM_WEIGHTS = {
3147 'repository.none': 0,
3148 'repository.read': 1,
3149 'repository.write': 3,
3150 'repository.admin': 4,
3151
3152 'group.none': 0,
3153 'group.read': 1,
3154 'group.write': 3,
3155 'group.admin': 4,
3156
3157 'usergroup.none': 0,
3158 'usergroup.read': 1,
3159 'usergroup.write': 3,
3160 'usergroup.admin': 4,
3161
3162 'branch.none': 0,
3163 'branch.merge': 1,
3164 'branch.push': 3,
3165 'branch.push_force': 4,
3166
3167 'hg.repogroup.create.false': 0,
3168 'hg.repogroup.create.true': 1,
3169
3170 'hg.usergroup.create.false': 0,
3171 'hg.usergroup.create.true': 1,
3172
3173 'hg.fork.none': 0,
3174 'hg.fork.repository': 1,
3175 'hg.create.none': 0,
3176 'hg.create.repository': 1
3177 }
3178
3179 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3180 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
3181 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
3182
3183 def __unicode__(self):
3184 return u"<%s('%s:%s')>" % (
3185 self.__class__.__name__, self.permission_id, self.permission_name
3186 )
3187
3188 @classmethod
3189 def get_by_key(cls, key):
3190 return cls.query().filter(cls.permission_name == key).scalar()
3191
3192 @classmethod
3193 def get_default_repo_perms(cls, user_id, repo_id=None):
3194 q = Session().query(UserRepoToPerm, Repository, Permission)\
3195 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
3196 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
3197 .filter(UserRepoToPerm.user_id == user_id)
3198 if repo_id:
3199 q = q.filter(UserRepoToPerm.repository_id == repo_id)
3200 return q.all()
3201
3202 @classmethod
3203 def get_default_repo_branch_perms(cls, user_id, repo_id=None):
3204 q = Session().query(UserToRepoBranchPermission, UserRepoToPerm, Permission) \
3205 .join(
3206 Permission,
3207 UserToRepoBranchPermission.permission_id == Permission.permission_id) \
3208 .join(
3209 UserRepoToPerm,
3210 UserToRepoBranchPermission.rule_to_perm_id == UserRepoToPerm.repo_to_perm_id) \
3211 .filter(UserRepoToPerm.user_id == user_id)
3212
3213 if repo_id:
3214 q = q.filter(UserToRepoBranchPermission.repository_id == repo_id)
3215 return q.order_by(UserToRepoBranchPermission.rule_order).all()
3216
3217 @classmethod
3218 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
3219 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
3220 .join(
3221 Permission,
3222 UserGroupRepoToPerm.permission_id == Permission.permission_id)\
3223 .join(
3224 Repository,
3225 UserGroupRepoToPerm.repository_id == Repository.repo_id)\
3226 .join(
3227 UserGroup,
3228 UserGroupRepoToPerm.users_group_id ==
3229 UserGroup.users_group_id)\
3230 .join(
3231 UserGroupMember,
3232 UserGroupRepoToPerm.users_group_id ==
3233 UserGroupMember.users_group_id)\
3234 .filter(
3235 UserGroupMember.user_id == user_id,
3236 UserGroup.users_group_active == true())
3237 if repo_id:
3238 q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
3239 return q.all()
3240
3241 @classmethod
3242 def get_default_repo_branch_perms_from_user_group(cls, user_id, repo_id=None):
3243 q = Session().query(UserGroupToRepoBranchPermission, UserGroupRepoToPerm, Permission) \
3244 .join(
3245 Permission,
3246 UserGroupToRepoBranchPermission.permission_id == Permission.permission_id) \
3247 .join(
3248 UserGroupRepoToPerm,
3249 UserGroupToRepoBranchPermission.rule_to_perm_id == UserGroupRepoToPerm.users_group_to_perm_id) \
3250 .join(
3251 UserGroup,
3252 UserGroupRepoToPerm.users_group_id == UserGroup.users_group_id) \
3253 .join(
3254 UserGroupMember,
3255 UserGroupRepoToPerm.users_group_id == UserGroupMember.users_group_id) \
3256 .filter(
3257 UserGroupMember.user_id == user_id,
3258 UserGroup.users_group_active == true())
3259
3260 if repo_id:
3261 q = q.filter(UserGroupToRepoBranchPermission.repository_id == repo_id)
3262 return q.order_by(UserGroupToRepoBranchPermission.rule_order).all()
3263
3264 @classmethod
3265 def get_default_group_perms(cls, user_id, repo_group_id=None):
3266 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
3267 .join(
3268 Permission,
3269 UserRepoGroupToPerm.permission_id == Permission.permission_id)\
3270 .join(
3271 RepoGroup,
3272 UserRepoGroupToPerm.group_id == RepoGroup.group_id)\
3273 .filter(UserRepoGroupToPerm.user_id == user_id)
3274 if repo_group_id:
3275 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
3276 return q.all()
3277
3278 @classmethod
3279 def get_default_group_perms_from_user_group(
3280 cls, user_id, repo_group_id=None):
3281 q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\
3282 .join(
3283 Permission,
3284 UserGroupRepoGroupToPerm.permission_id ==
3285 Permission.permission_id)\
3286 .join(
3287 RepoGroup,
3288 UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\
3289 .join(
3290 UserGroup,
3291 UserGroupRepoGroupToPerm.users_group_id ==
3292 UserGroup.users_group_id)\
3293 .join(
3294 UserGroupMember,
3295 UserGroupRepoGroupToPerm.users_group_id ==
3296 UserGroupMember.users_group_id)\
3297 .filter(
3298 UserGroupMember.user_id == user_id,
3299 UserGroup.users_group_active == true())
3300 if repo_group_id:
3301 q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id)
3302 return q.all()
3303
3304 @classmethod
3305 def get_default_user_group_perms(cls, user_id, user_group_id=None):
3306 q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\
3307 .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\
3308 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
3309 .filter(UserUserGroupToPerm.user_id == user_id)
3310 if user_group_id:
3311 q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id)
3312 return q.all()
3313
3314 @classmethod
3315 def get_default_user_group_perms_from_user_group(
3316 cls, user_id, user_group_id=None):
3317 TargetUserGroup = aliased(UserGroup, name='target_user_group')
3318 q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\
3319 .join(
3320 Permission,
3321 UserGroupUserGroupToPerm.permission_id ==
3322 Permission.permission_id)\
3323 .join(
3324 TargetUserGroup,
3325 UserGroupUserGroupToPerm.target_user_group_id ==
3326 TargetUserGroup.users_group_id)\
3327 .join(
3328 UserGroup,
3329 UserGroupUserGroupToPerm.user_group_id ==
3330 UserGroup.users_group_id)\
3331 .join(
3332 UserGroupMember,
3333 UserGroupUserGroupToPerm.user_group_id ==
3334 UserGroupMember.users_group_id)\
3335 .filter(
3336 UserGroupMember.user_id == user_id,
3337 UserGroup.users_group_active == true())
3338 if user_group_id:
3339 q = q.filter(
3340 UserGroupUserGroupToPerm.user_group_id == user_group_id)
3341
3342 return q.all()
3343
3344
3345 class UserRepoToPerm(Base, BaseModel):
3346 __tablename__ = 'repo_to_perm'
3347 __table_args__ = (
3348 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
3349 base_table_args
3350 )
3351
3352 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3353 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3354 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3355 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
3356
3357 user = relationship('User')
3358 repository = relationship('Repository')
3359 permission = relationship('Permission')
3360
3361 branch_perm_entry = relationship('UserToRepoBranchPermission', cascade="all, delete-orphan", lazy='joined')
3362
3363 @classmethod
3364 def create(cls, user, repository, permission):
3365 n = cls()
3366 n.user = user
3367 n.repository = repository
3368 n.permission = permission
3369 Session().add(n)
3370 return n
3371
3372 def __unicode__(self):
3373 return u'<%s => %s >' % (self.user, self.repository)
3374
3375
3376 class UserUserGroupToPerm(Base, BaseModel):
3377 __tablename__ = 'user_user_group_to_perm'
3378 __table_args__ = (
3379 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
3380 base_table_args
3381 )
3382
3383 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3384 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3385 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3386 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3387
3388 user = relationship('User')
3389 user_group = relationship('UserGroup')
3390 permission = relationship('Permission')
3391
3392 @classmethod
3393 def create(cls, user, user_group, permission):
3394 n = cls()
3395 n.user = user
3396 n.user_group = user_group
3397 n.permission = permission
3398 Session().add(n)
3399 return n
3400
3401 def __unicode__(self):
3402 return u'<%s => %s >' % (self.user, self.user_group)
3403
3404
3405 class UserToPerm(Base, BaseModel):
3406 __tablename__ = 'user_to_perm'
3407 __table_args__ = (
3408 UniqueConstraint('user_id', 'permission_id'),
3409 base_table_args
3410 )
3411
3412 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3413 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3414 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3415
3416 user = relationship('User')
3417 permission = relationship('Permission', lazy='joined')
3418
3419 def __unicode__(self):
3420 return u'<%s => %s >' % (self.user, self.permission)
3421
3422
3423 class UserGroupRepoToPerm(Base, BaseModel):
3424 __tablename__ = 'users_group_repo_to_perm'
3425 __table_args__ = (
3426 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
3427 base_table_args
3428 )
3429
3430 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3431 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3432 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3433 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
3434
3435 users_group = relationship('UserGroup')
3436 permission = relationship('Permission')
3437 repository = relationship('Repository')
3438 user_group_branch_perms = relationship('UserGroupToRepoBranchPermission', cascade='all')
3439
3440 @classmethod
3441 def create(cls, users_group, repository, permission):
3442 n = cls()
3443 n.users_group = users_group
3444 n.repository = repository
3445 n.permission = permission
3446 Session().add(n)
3447 return n
3448
3449 def __unicode__(self):
3450 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
3451
3452
3453 class UserGroupUserGroupToPerm(Base, BaseModel):
3454 __tablename__ = 'user_group_user_group_to_perm'
3455 __table_args__ = (
3456 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
3457 CheckConstraint('target_user_group_id != user_group_id'),
3458 base_table_args
3459 )
3460
3461 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3462 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3463 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3464 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3465
3466 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
3467 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
3468 permission = relationship('Permission')
3469
3470 @classmethod
3471 def create(cls, target_user_group, user_group, permission):
3472 n = cls()
3473 n.target_user_group = target_user_group
3474 n.user_group = user_group
3475 n.permission = permission
3476 Session().add(n)
3477 return n
3478
3479 def __unicode__(self):
3480 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
3481
3482
3483 class UserGroupToPerm(Base, BaseModel):
3484 __tablename__ = 'users_group_to_perm'
3485 __table_args__ = (
3486 UniqueConstraint('users_group_id', 'permission_id',),
3487 base_table_args
3488 )
3489
3490 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3491 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3492 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3493
3494 users_group = relationship('UserGroup')
3495 permission = relationship('Permission')
3496
3497
3498 class UserRepoGroupToPerm(Base, BaseModel):
3499 __tablename__ = 'user_repo_group_to_perm'
3500 __table_args__ = (
3501 UniqueConstraint('user_id', 'group_id', 'permission_id'),
3502 base_table_args
3503 )
3504
3505 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3506 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
3508 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3509
3510 user = relationship('User')
3511 group = relationship('RepoGroup')
3512 permission = relationship('Permission')
3513
3514 @classmethod
3515 def create(cls, user, repository_group, permission):
3516 n = cls()
3517 n.user = user
3518 n.group = repository_group
3519 n.permission = permission
3520 Session().add(n)
3521 return n
3522
3523
3524 class UserGroupRepoGroupToPerm(Base, BaseModel):
3525 __tablename__ = 'users_group_repo_group_to_perm'
3526 __table_args__ = (
3527 UniqueConstraint('users_group_id', 'group_id'),
3528 base_table_args
3529 )
3530
3531 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)
3532 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3533 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
3534 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3535
3536 users_group = relationship('UserGroup')
3537 permission = relationship('Permission')
3538 group = relationship('RepoGroup')
3539
3540 @classmethod
3541 def create(cls, user_group, repository_group, permission):
3542 n = cls()
3543 n.users_group = user_group
3544 n.group = repository_group
3545 n.permission = permission
3546 Session().add(n)
3547 return n
3548
3549 def __unicode__(self):
3550 return u'<UserGroupRepoGroupToPerm:%s => %s >' % (self.users_group, self.group)
3551
3552
3553 class Statistics(Base, BaseModel):
3554 __tablename__ = 'statistics'
3555 __table_args__ = (
3556 base_table_args
3557 )
3558
3559 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3560 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
3561 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
3562 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
3563 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
3564 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
3565
3566 repository = relationship('Repository', single_parent=True)
3567
3568
3569 class UserFollowing(Base, BaseModel):
3570 __tablename__ = 'user_followings'
3571 __table_args__ = (
3572 UniqueConstraint('user_id', 'follows_repository_id'),
3573 UniqueConstraint('user_id', 'follows_user_id'),
3574 base_table_args
3575 )
3576
3577 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3578 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3579 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
3580 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
3581 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
3582
3583 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
3584
3585 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
3586 follows_repository = relationship('Repository', order_by='Repository.repo_name')
3587
3588 @classmethod
3589 def get_repo_followers(cls, repo_id):
3590 return cls.query().filter(cls.follows_repo_id == repo_id)
3591
3592
3593 class CacheKey(Base, BaseModel):
3594 __tablename__ = 'cache_invalidation'
3595 __table_args__ = (
3596 UniqueConstraint('cache_key'),
3597 Index('key_idx', 'cache_key'),
3598 base_table_args,
3599 )
3600
3601 CACHE_TYPE_FEED = 'FEED'
3602
3603 # namespaces used to register process/thread aware caches
3604 REPO_INVALIDATION_NAMESPACE = 'repo_cache:{repo_id}'
3605 SETTINGS_INVALIDATION_NAMESPACE = 'system_settings'
3606
3607 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3608 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
3609 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
3610 cache_state_uid = Column("cache_state_uid", String(255), nullable=True, unique=None, default=None)
3611 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
3612
3613 def __init__(self, cache_key, cache_args='', cache_state_uid=None):
3614 self.cache_key = cache_key
3615 self.cache_args = cache_args
3616 self.cache_active = False
3617 # first key should be same for all entries, since all workers should share it
3618 self.cache_state_uid = cache_state_uid or self.generate_new_state_uid()
3619
3620 def __unicode__(self):
3621 return u"<%s('%s:%s[%s]')>" % (
3622 self.__class__.__name__,
3623 self.cache_id, self.cache_key, self.cache_active)
3624
3625 def _cache_key_partition(self):
3626 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
3627 return prefix, repo_name, suffix
3628
3629 def get_prefix(self):
3630 """
3631 Try to extract prefix from existing cache key. The key could consist
3632 of prefix, repo_name, suffix
3633 """
3634 # this returns prefix, repo_name, suffix
3635 return self._cache_key_partition()[0]
3636
3637 def get_suffix(self):
3638 """
3639 get suffix that might have been used in _get_cache_key to
3640 generate self.cache_key. Only used for informational purposes
3641 in repo_edit.mako.
3642 """
3643 # prefix, repo_name, suffix
3644 return self._cache_key_partition()[2]
3645
3646 @classmethod
3647 def generate_new_state_uid(cls, based_on=None):
3648 if based_on:
3649 return str(uuid.uuid5(uuid.NAMESPACE_URL, safe_str(based_on)))
3650 else:
3651 return str(uuid.uuid4())
3652
3653 @classmethod
3654 def delete_all_cache(cls):
3655 """
3656 Delete all cache keys from database.
3657 Should only be run when all instances are down and all entries
3658 thus stale.
3659 """
3660 cls.query().delete()
3661 Session().commit()
3662
3663 @classmethod
3664 def set_invalidate(cls, cache_uid, delete=False):
3665 """
3666 Mark all caches of a repo as invalid in the database.
3667 """
3668
3669 try:
3670 qry = Session().query(cls).filter(cls.cache_args == cache_uid)
3671 if delete:
3672 qry.delete()
3673 log.debug('cache objects deleted for cache args %s',
3674 safe_str(cache_uid))
3675 else:
3676 qry.update({"cache_active": False,
3677 "cache_state_uid": cls.generate_new_state_uid()})
3678 log.debug('cache objects marked as invalid for cache args %s',
3679 safe_str(cache_uid))
3680
3681 Session().commit()
3682 except Exception:
3683 log.exception(
3684 'Cache key invalidation failed for cache args %s',
3685 safe_str(cache_uid))
3686 Session().rollback()
3687
3688 @classmethod
3689 def get_active_cache(cls, cache_key):
3690 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
3691 if inv_obj:
3692 return inv_obj
3693 return None
3694
3695 @classmethod
3696 def get_namespace_map(cls, namespace):
3697 return {
3698 x.cache_key: x
3699 for x in cls.query().filter(cls.cache_args == namespace)}
3700
3701
3702 class ChangesetComment(Base, BaseModel):
3703 __tablename__ = 'changeset_comments'
3704 __table_args__ = (
3705 Index('cc_revision_idx', 'revision'),
3706 base_table_args,
3707 )
3708
3709 COMMENT_OUTDATED = u'comment_outdated'
3710 COMMENT_TYPE_NOTE = u'note'
3711 COMMENT_TYPE_TODO = u'todo'
3712 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3713
3714 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3715 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3716 revision = Column('revision', String(40), nullable=True)
3717 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3718 pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True)
3719 line_no = Column('line_no', Unicode(10), nullable=True)
3720 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
3721 f_path = Column('f_path', Unicode(1000), nullable=True)
3722 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
3723 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
3724 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3725 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3726 renderer = Column('renderer', Unicode(64), nullable=True)
3727 display_state = Column('display_state', Unicode(128), nullable=True)
3728
3729 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3730 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
3731
3732 resolved_comment = relationship('ChangesetComment', remote_side=comment_id, back_populates='resolved_by')
3733 resolved_by = relationship('ChangesetComment', back_populates='resolved_comment')
3734
3735 author = relationship('User', lazy='joined')
3736 repo = relationship('Repository')
3737 status_change = relationship('ChangesetStatus', cascade="all, delete-orphan", lazy='joined')
3738 pull_request = relationship('PullRequest', lazy='joined')
3739 pull_request_version = relationship('PullRequestVersion')
3740
3741 @classmethod
3742 def get_users(cls, revision=None, pull_request_id=None):
3743 """
3744 Returns user associated with this ChangesetComment. ie those
3745 who actually commented
3746
3747 :param cls:
3748 :param revision:
3749 """
3750 q = Session().query(User)\
3751 .join(ChangesetComment.author)
3752 if revision:
3753 q = q.filter(cls.revision == revision)
3754 elif pull_request_id:
3755 q = q.filter(cls.pull_request_id == pull_request_id)
3756 return q.all()
3757
3758 @classmethod
3759 def get_index_from_version(cls, pr_version, versions):
3760 num_versions = [x.pull_request_version_id for x in versions]
3761 try:
3762 return num_versions.index(pr_version) +1
3763 except (IndexError, ValueError):
3764 return
3765
3766 @property
3767 def outdated(self):
3768 return self.display_state == self.COMMENT_OUTDATED
3769
3770 def outdated_at_version(self, version):
3771 """
3772 Checks if comment is outdated for given pull request version
3773 """
3774 return self.outdated and self.pull_request_version_id != version
3775
3776 def older_than_version(self, version):
3777 """
3778 Checks if comment is made from previous version than given
3779 """
3780 if version is None:
3781 return self.pull_request_version_id is not None
3782
3783 return self.pull_request_version_id < version
3784
3785 @property
3786 def resolved(self):
3787 return self.resolved_by[0] if self.resolved_by else None
3788
3789 @property
3790 def is_todo(self):
3791 return self.comment_type == self.COMMENT_TYPE_TODO
3792
3793 @property
3794 def is_inline(self):
3795 return self.line_no and self.f_path
3796
3797 def get_index_version(self, versions):
3798 return self.get_index_from_version(
3799 self.pull_request_version_id, versions)
3800
3801 def __repr__(self):
3802 if self.comment_id:
3803 return '<DB:Comment #%s>' % self.comment_id
3804 else:
3805 return '<DB:Comment at %#x>' % id(self)
3806
3807 def get_api_data(self):
3808 comment = self
3809 data = {
3810 'comment_id': comment.comment_id,
3811 'comment_type': comment.comment_type,
3812 'comment_text': comment.text,
3813 'comment_status': comment.status_change,
3814 'comment_f_path': comment.f_path,
3815 'comment_lineno': comment.line_no,
3816 'comment_author': comment.author,
3817 'comment_created_on': comment.created_on,
3818 'comment_resolved_by': self.resolved
3819 }
3820 return data
3821
3822 def __json__(self):
3823 data = dict()
3824 data.update(self.get_api_data())
3825 return data
3826
3827
3828 class ChangesetStatus(Base, BaseModel):
3829 __tablename__ = 'changeset_statuses'
3830 __table_args__ = (
3831 Index('cs_revision_idx', 'revision'),
3832 Index('cs_version_idx', 'version'),
3833 UniqueConstraint('repo_id', 'revision', 'version'),
3834 base_table_args
3835 )
3836
3837 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
3838 STATUS_APPROVED = 'approved'
3839 STATUS_REJECTED = 'rejected'
3840 STATUS_UNDER_REVIEW = 'under_review'
3841
3842 STATUSES = [
3843 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
3844 (STATUS_APPROVED, _("Approved")),
3845 (STATUS_REJECTED, _("Rejected")),
3846 (STATUS_UNDER_REVIEW, _("Under Review")),
3847 ]
3848
3849 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
3850 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3851 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
3852 revision = Column('revision', String(40), nullable=False)
3853 status = Column('status', String(128), nullable=False, default=DEFAULT)
3854 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
3855 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
3856 version = Column('version', Integer(), nullable=False, default=0)
3857 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3858
3859 author = relationship('User', lazy='joined')
3860 repo = relationship('Repository')
3861 comment = relationship('ChangesetComment', lazy='joined')
3862 pull_request = relationship('PullRequest', lazy='joined')
3863
3864 def __unicode__(self):
3865 return u"<%s('%s[v%s]:%s')>" % (
3866 self.__class__.__name__,
3867 self.status, self.version, self.author
3868 )
3869
3870 @classmethod
3871 def get_status_lbl(cls, value):
3872 return dict(cls.STATUSES).get(value)
3873
3874 @property
3875 def status_lbl(self):
3876 return ChangesetStatus.get_status_lbl(self.status)
3877
3878 def get_api_data(self):
3879 status = self
3880 data = {
3881 'status_id': status.changeset_status_id,
3882 'status': status.status,
3883 }
3884 return data
3885
3886 def __json__(self):
3887 data = dict()
3888 data.update(self.get_api_data())
3889 return data
3890
3891
3892 class _SetState(object):
3893 """
3894 Context processor allowing changing state for sensitive operation such as
3895 pull request update or merge
3896 """
3897
3898 def __init__(self, pull_request, pr_state, back_state=None):
3899 self._pr = pull_request
3900 self._org_state = back_state or pull_request.pull_request_state
3901 self._pr_state = pr_state
3902 self._current_state = None
3903
3904 def __enter__(self):
3905 log.debug('StateLock: entering set state context of pr %s, setting state to: `%s`',
3906 self._pr, self._pr_state)
3907 self.set_pr_state(self._pr_state)
3908 return self
3909
3910 def __exit__(self, exc_type, exc_val, exc_tb):
3911 if exc_val is not None:
3912 log.error(traceback.format_exc(exc_tb))
3913 return None
3914
3915 self.set_pr_state(self._org_state)
3916 log.debug('StateLock: exiting set state context of pr %s, setting state to: `%s`',
3917 self._pr, self._org_state)
3918
3919 @property
3920 def state(self):
3921 return self._current_state
3922
3923 def set_pr_state(self, pr_state):
3924 try:
3925 self._pr.pull_request_state = pr_state
3926 Session().add(self._pr)
3927 Session().commit()
3928 self._current_state = pr_state
3929 except Exception:
3930 log.exception('Failed to set PullRequest %s state to %s', self._pr, pr_state)
3931 raise
3932
3933
3934 class _PullRequestBase(BaseModel):
3935 """
3936 Common attributes of pull request and version entries.
3937 """
3938
3939 # .status values
3940 STATUS_NEW = u'new'
3941 STATUS_OPEN = u'open'
3942 STATUS_CLOSED = u'closed'
3943
3944 # available states
3945 STATE_CREATING = u'creating'
3946 STATE_UPDATING = u'updating'
3947 STATE_MERGING = u'merging'
3948 STATE_CREATED = u'created'
3949
3950 title = Column('title', Unicode(255), nullable=True)
3951 description = Column(
3952 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'),
3953 nullable=True)
3954 description_renderer = Column('description_renderer', Unicode(64), nullable=True)
3955
3956 # new/open/closed status of pull request (not approve/reject/etc)
3957 status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW)
3958 created_on = Column(
3959 'created_on', DateTime(timezone=False), nullable=False,
3960 default=datetime.datetime.now)
3961 updated_on = Column(
3962 'updated_on', DateTime(timezone=False), nullable=False,
3963 default=datetime.datetime.now)
3964
3965 pull_request_state = Column("pull_request_state", String(255), nullable=True)
3966
3967 @declared_attr
3968 def user_id(cls):
3969 return Column(
3970 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
3971 unique=None)
3972
3973 # 500 revisions max
3974 _revisions = Column(
3975 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
3976
3977 @declared_attr
3978 def source_repo_id(cls):
3979 # TODO: dan: rename column to source_repo_id
3980 return Column(
3981 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3982 nullable=False)
3983
3984 _source_ref = Column('org_ref', Unicode(255), nullable=False)
3985
3986 @hybrid_property
3987 def source_ref(self):
3988 return self._source_ref
3989
3990 @source_ref.setter
3991 def source_ref(self, val):
3992 parts = (val or '').split(':')
3993 if len(parts) != 3:
3994 raise ValueError(
3995 'Invalid reference format given: {}, expected X:Y:Z'.format(val))
3996 self._source_ref = safe_unicode(val)
3997
3998 _target_ref = Column('other_ref', Unicode(255), nullable=False)
3999
4000 @hybrid_property
4001 def target_ref(self):
4002 return self._target_ref
4003
4004 @target_ref.setter
4005 def target_ref(self, val):
4006 parts = (val or '').split(':')
4007 if len(parts) != 3:
4008 raise ValueError(
4009 'Invalid reference format given: {}, expected X:Y:Z'.format(val))
4010 self._target_ref = safe_unicode(val)
4011
4012 @declared_attr
4013 def target_repo_id(cls):
4014 # TODO: dan: rename column to target_repo_id
4015 return Column(
4016 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'),
4017 nullable=False)
4018
4019 _shadow_merge_ref = Column('shadow_merge_ref', Unicode(255), nullable=True)
4020
4021 # TODO: dan: rename column to last_merge_source_rev
4022 _last_merge_source_rev = Column(
4023 'last_merge_org_rev', String(40), nullable=True)
4024 # TODO: dan: rename column to last_merge_target_rev
4025 _last_merge_target_rev = Column(
4026 'last_merge_other_rev', String(40), nullable=True)
4027 _last_merge_status = Column('merge_status', Integer(), nullable=True)
4028 last_merge_metadata = Column(
4029 'last_merge_metadata', MutationObj.as_mutable(
4030 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4031
4032 merge_rev = Column('merge_rev', String(40), nullable=True)
4033
4034 reviewer_data = Column(
4035 'reviewer_data_json', MutationObj.as_mutable(
4036 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4037
4038 @property
4039 def reviewer_data_json(self):
4040 return json.dumps(self.reviewer_data)
4041
4042 @property
4043 def work_in_progress(self):
4044 """checks if pull request is work in progress by checking the title"""
4045 title = self.title.upper()
4046 if re.match(r'^(\[WIP\]\s*|WIP:\s*|WIP\s+)', title):
4047 return True
4048 return False
4049
4050 @hybrid_property
4051 def description_safe(self):
4052 from rhodecode.lib import helpers as h
4053 return h.escape(self.description)
4054
4055 @hybrid_property
4056 def revisions(self):
4057 return self._revisions.split(':') if self._revisions else []
4058
4059 @revisions.setter
4060 def revisions(self, val):
4061 self._revisions = u':'.join(val)
4062
4063 @hybrid_property
4064 def last_merge_status(self):
4065 return safe_int(self._last_merge_status)
4066
4067 @last_merge_status.setter
4068 def last_merge_status(self, val):
4069 self._last_merge_status = val
4070
4071 @declared_attr
4072 def author(cls):
4073 return relationship('User', lazy='joined')
4074
4075 @declared_attr
4076 def source_repo(cls):
4077 return relationship(
4078 'Repository',
4079 primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__)
4080
4081 @property
4082 def source_ref_parts(self):
4083 return self.unicode_to_reference(self.source_ref)
4084
4085 @declared_attr
4086 def target_repo(cls):
4087 return relationship(
4088 'Repository',
4089 primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__)
4090
4091 @property
4092 def target_ref_parts(self):
4093 return self.unicode_to_reference(self.target_ref)
4094
4095 @property
4096 def shadow_merge_ref(self):
4097 return self.unicode_to_reference(self._shadow_merge_ref)
4098
4099 @shadow_merge_ref.setter
4100 def shadow_merge_ref(self, ref):
4101 self._shadow_merge_ref = self.reference_to_unicode(ref)
4102
4103 @staticmethod
4104 def unicode_to_reference(raw):
4105 """
4106 Convert a unicode (or string) to a reference object.
4107 If unicode evaluates to False it returns None.
4108 """
4109 if raw:
4110 refs = raw.split(':')
4111 return Reference(*refs)
4112 else:
4113 return None
4114
4115 @staticmethod
4116 def reference_to_unicode(ref):
4117 """
4118 Convert a reference object to unicode.
4119 If reference is None it returns None.
4120 """
4121 if ref:
4122 return u':'.join(ref)
4123 else:
4124 return None
4125
4126 def get_api_data(self, with_merge_state=True):
4127 from rhodecode.model.pull_request import PullRequestModel
4128
4129 pull_request = self
4130 if with_merge_state:
4131 merge_response, merge_status, msg = \
4132 PullRequestModel().merge_status(pull_request)
4133 merge_state = {
4134 'status': merge_status,
4135 'message': safe_unicode(msg),
4136 }
4137 else:
4138 merge_state = {'status': 'not_available',
4139 'message': 'not_available'}
4140
4141 merge_data = {
4142 'clone_url': PullRequestModel().get_shadow_clone_url(pull_request),
4143 'reference': (
4144 pull_request.shadow_merge_ref._asdict()
4145 if pull_request.shadow_merge_ref else None),
4146 }
4147
4148 data = {
4149 'pull_request_id': pull_request.pull_request_id,
4150 'url': PullRequestModel().get_url(pull_request),
4151 'title': pull_request.title,
4152 'description': pull_request.description,
4153 'status': pull_request.status,
4154 'state': pull_request.pull_request_state,
4155 'created_on': pull_request.created_on,
4156 'updated_on': pull_request.updated_on,
4157 'commit_ids': pull_request.revisions,
4158 'review_status': pull_request.calculated_review_status(),
4159 'mergeable': merge_state,
4160 'source': {
4161 'clone_url': pull_request.source_repo.clone_url(),
4162 'repository': pull_request.source_repo.repo_name,
4163 'reference': {
4164 'name': pull_request.source_ref_parts.name,
4165 'type': pull_request.source_ref_parts.type,
4166 'commit_id': pull_request.source_ref_parts.commit_id,
4167 },
4168 },
4169 'target': {
4170 'clone_url': pull_request.target_repo.clone_url(),
4171 'repository': pull_request.target_repo.repo_name,
4172 'reference': {
4173 'name': pull_request.target_ref_parts.name,
4174 'type': pull_request.target_ref_parts.type,
4175 'commit_id': pull_request.target_ref_parts.commit_id,
4176 },
4177 },
4178 'merge': merge_data,
4179 'author': pull_request.author.get_api_data(include_secrets=False,
4180 details='basic'),
4181 'reviewers': [
4182 {
4183 'user': reviewer.get_api_data(include_secrets=False,
4184 details='basic'),
4185 'reasons': reasons,
4186 'review_status': st[0][1].status if st else 'not_reviewed',
4187 }
4188 for obj, reviewer, reasons, mandatory, st in
4189 pull_request.reviewers_statuses()
4190 ]
4191 }
4192
4193 return data
4194
4195 def set_state(self, pull_request_state, final_state=None):
4196 """
4197 # goes from initial state to updating to initial state.
4198 # initial state can be changed by specifying back_state=
4199 with pull_request_obj.set_state(PullRequest.STATE_UPDATING):
4200 pull_request.merge()
4201
4202 :param pull_request_state:
4203 :param final_state:
4204
4205 """
4206
4207 return _SetState(self, pull_request_state, back_state=final_state)
4208
4209
4210 class PullRequest(Base, _PullRequestBase):
4211 __tablename__ = 'pull_requests'
4212 __table_args__ = (
4213 base_table_args,
4214 )
4215
4216 pull_request_id = Column(
4217 'pull_request_id', Integer(), nullable=False, primary_key=True)
4218
4219 def __repr__(self):
4220 if self.pull_request_id:
4221 return '<DB:PullRequest #%s>' % self.pull_request_id
4222 else:
4223 return '<DB:PullRequest at %#x>' % id(self)
4224
4225 reviewers = relationship('PullRequestReviewers', cascade="all, delete-orphan")
4226 statuses = relationship('ChangesetStatus', cascade="all, delete-orphan")
4227 comments = relationship('ChangesetComment', cascade="all, delete-orphan")
4228 versions = relationship('PullRequestVersion', cascade="all, delete-orphan",
4229 lazy='dynamic')
4230
4231 @classmethod
4232 def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj,
4233 internal_methods=None):
4234
4235 class PullRequestDisplay(object):
4236 """
4237 Special object wrapper for showing PullRequest data via Versions
4238 It mimics PR object as close as possible. This is read only object
4239 just for display
4240 """
4241
4242 def __init__(self, attrs, internal=None):
4243 self.attrs = attrs
4244 # internal have priority over the given ones via attrs
4245 self.internal = internal or ['versions']
4246
4247 def __getattr__(self, item):
4248 if item in self.internal:
4249 return getattr(self, item)
4250 try:
4251 return self.attrs[item]
4252 except KeyError:
4253 raise AttributeError(
4254 '%s object has no attribute %s' % (self, item))
4255
4256 def __repr__(self):
4257 return '<DB:PullRequestDisplay #%s>' % self.attrs.get('pull_request_id')
4258
4259 def versions(self):
4260 return pull_request_obj.versions.order_by(
4261 PullRequestVersion.pull_request_version_id).all()
4262
4263 def is_closed(self):
4264 return pull_request_obj.is_closed()
4265
4266 def is_state_changing(self):
4267 return pull_request_obj.is_state_changing()
4268
4269 @property
4270 def pull_request_version_id(self):
4271 return getattr(pull_request_obj, 'pull_request_version_id', None)
4272
4273 attrs = StrictAttributeDict(pull_request_obj.get_api_data(with_merge_state=False))
4274
4275 attrs.author = StrictAttributeDict(
4276 pull_request_obj.author.get_api_data())
4277 if pull_request_obj.target_repo:
4278 attrs.target_repo = StrictAttributeDict(
4279 pull_request_obj.target_repo.get_api_data())
4280 attrs.target_repo.clone_url = pull_request_obj.target_repo.clone_url
4281
4282 if pull_request_obj.source_repo:
4283 attrs.source_repo = StrictAttributeDict(
4284 pull_request_obj.source_repo.get_api_data())
4285 attrs.source_repo.clone_url = pull_request_obj.source_repo.clone_url
4286
4287 attrs.source_ref_parts = pull_request_obj.source_ref_parts
4288 attrs.target_ref_parts = pull_request_obj.target_ref_parts
4289 attrs.revisions = pull_request_obj.revisions
4290
4291 attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref
4292 attrs.reviewer_data = org_pull_request_obj.reviewer_data
4293 attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json
4294
4295 return PullRequestDisplay(attrs, internal=internal_methods)
4296
4297 def is_closed(self):
4298 return self.status == self.STATUS_CLOSED
4299
4300 def is_state_changing(self):
4301 return self.pull_request_state != PullRequest.STATE_CREATED
4302
4303 def __json__(self):
4304 return {
4305 'revisions': self.revisions,
4306 'versions': self.versions_count
4307 }
4308
4309 def calculated_review_status(self):
4310 from rhodecode.model.changeset_status import ChangesetStatusModel
4311 return ChangesetStatusModel().calculated_review_status(self)
4312
4313 def reviewers_statuses(self):
4314 from rhodecode.model.changeset_status import ChangesetStatusModel
4315 return ChangesetStatusModel().reviewers_statuses(self)
4316
4317 @property
4318 def workspace_id(self):
4319 from rhodecode.model.pull_request import PullRequestModel
4320 return PullRequestModel()._workspace_id(self)
4321
4322 def get_shadow_repo(self):
4323 workspace_id = self.workspace_id
4324 shadow_repository_path = self.target_repo.get_shadow_repository_path(workspace_id)
4325 if os.path.isdir(shadow_repository_path):
4326 vcs_obj = self.target_repo.scm_instance()
4327 return vcs_obj.get_shadow_instance(shadow_repository_path)
4328
4329 @property
4330 def versions_count(self):
4331 """
4332 return number of versions this PR have, e.g a PR that once been
4333 updated will have 2 versions
4334 """
4335 return self.versions.count() + 1
4336
4337
4338 class PullRequestVersion(Base, _PullRequestBase):
4339 __tablename__ = 'pull_request_versions'
4340 __table_args__ = (
4341 base_table_args,
4342 )
4343
4344 pull_request_version_id = Column(
4345 'pull_request_version_id', Integer(), nullable=False, primary_key=True)
4346 pull_request_id = Column(
4347 'pull_request_id', Integer(),
4348 ForeignKey('pull_requests.pull_request_id'), nullable=False)
4349 pull_request = relationship('PullRequest')
4350
4351 def __repr__(self):
4352 if self.pull_request_version_id:
4353 return '<DB:PullRequestVersion #%s>' % self.pull_request_version_id
4354 else:
4355 return '<DB:PullRequestVersion at %#x>' % id(self)
4356
4357 @property
4358 def reviewers(self):
4359 return self.pull_request.reviewers
4360
4361 @property
4362 def versions(self):
4363 return self.pull_request.versions
4364
4365 def is_closed(self):
4366 # calculate from original
4367 return self.pull_request.status == self.STATUS_CLOSED
4368
4369 def is_state_changing(self):
4370 return self.pull_request.pull_request_state != PullRequest.STATE_CREATED
4371
4372 def calculated_review_status(self):
4373 return self.pull_request.calculated_review_status()
4374
4375 def reviewers_statuses(self):
4376 return self.pull_request.reviewers_statuses()
4377
4378
4379 class PullRequestReviewers(Base, BaseModel):
4380 __tablename__ = 'pull_request_reviewers'
4381 __table_args__ = (
4382 base_table_args,
4383 )
4384
4385 @hybrid_property
4386 def reasons(self):
4387 if not self._reasons:
4388 return []
4389 return self._reasons
4390
4391 @reasons.setter
4392 def reasons(self, val):
4393 val = val or []
4394 if any(not isinstance(x, compat.string_types) for x in val):
4395 raise Exception('invalid reasons type, must be list of strings')
4396 self._reasons = val
4397
4398 pull_requests_reviewers_id = Column(
4399 'pull_requests_reviewers_id', Integer(), nullable=False,
4400 primary_key=True)
4401 pull_request_id = Column(
4402 "pull_request_id", Integer(),
4403 ForeignKey('pull_requests.pull_request_id'), nullable=False)
4404 user_id = Column(
4405 "user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
4406 _reasons = Column(
4407 'reason', MutationList.as_mutable(
4408 JsonType('list', dialect_map=dict(mysql=UnicodeText(16384)))))
4409
4410 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4411 user = relationship('User')
4412 pull_request = relationship('PullRequest')
4413
4414 rule_data = Column(
4415 'rule_data_json',
4416 JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
4417
4418 def rule_user_group_data(self):
4419 """
4420 Returns the voting user group rule data for this reviewer
4421 """
4422
4423 if self.rule_data and 'vote_rule' in self.rule_data:
4424 user_group_data = {}
4425 if 'rule_user_group_entry_id' in self.rule_data:
4426 # means a group with voting rules !
4427 user_group_data['id'] = self.rule_data['rule_user_group_entry_id']
4428 user_group_data['name'] = self.rule_data['rule_name']
4429 user_group_data['vote_rule'] = self.rule_data['vote_rule']
4430
4431 return user_group_data
4432
4433 def __unicode__(self):
4434 return u"<%s('id:%s')>" % (self.__class__.__name__,
4435 self.pull_requests_reviewers_id)
4436
4437
4438 class Notification(Base, BaseModel):
4439 __tablename__ = 'notifications'
4440 __table_args__ = (
4441 Index('notification_type_idx', 'type'),
4442 base_table_args,
4443 )
4444
4445 TYPE_CHANGESET_COMMENT = u'cs_comment'
4446 TYPE_MESSAGE = u'message'
4447 TYPE_MENTION = u'mention'
4448 TYPE_REGISTRATION = u'registration'
4449 TYPE_PULL_REQUEST = u'pull_request'
4450 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
4451 TYPE_PULL_REQUEST_UPDATE = u'pull_request_update'
4452
4453 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
4454 subject = Column('subject', Unicode(512), nullable=True)
4455 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
4456 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
4457 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4458 type_ = Column('type', Unicode(255))
4459
4460 created_by_user = relationship('User')
4461 notifications_to_users = relationship('UserNotification', lazy='joined',
4462 cascade="all, delete-orphan")
4463
4464 @property
4465 def recipients(self):
4466 return [x.user for x in UserNotification.query()\
4467 .filter(UserNotification.notification == self)\
4468 .order_by(UserNotification.user_id.asc()).all()]
4469
4470 @classmethod
4471 def create(cls, created_by, subject, body, recipients, type_=None):
4472 if type_ is None:
4473 type_ = Notification.TYPE_MESSAGE
4474
4475 notification = cls()
4476 notification.created_by_user = created_by
4477 notification.subject = subject
4478 notification.body = body
4479 notification.type_ = type_
4480 notification.created_on = datetime.datetime.now()
4481
4482 # For each recipient link the created notification to his account
4483 for u in recipients:
4484 assoc = UserNotification()
4485 assoc.user_id = u.user_id
4486 assoc.notification = notification
4487
4488 # if created_by is inside recipients mark his notification
4489 # as read
4490 if u.user_id == created_by.user_id:
4491 assoc.read = True
4492 Session().add(assoc)
4493
4494 Session().add(notification)
4495
4496 return notification
4497
4498
4499 class UserNotification(Base, BaseModel):
4500 __tablename__ = 'user_to_notification'
4501 __table_args__ = (
4502 UniqueConstraint('user_id', 'notification_id'),
4503 base_table_args
4504 )
4505
4506 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
4507 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
4508 read = Column('read', Boolean, default=False)
4509 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
4510
4511 user = relationship('User', lazy="joined")
4512 notification = relationship('Notification', lazy="joined",
4513 order_by=lambda: Notification.created_on.desc(),)
4514
4515 def mark_as_read(self):
4516 self.read = True
4517 Session().add(self)
4518
4519
4520 class UserNotice(Base, BaseModel):
4521 __tablename__ = 'user_notices'
4522 __table_args__ = (
4523 base_table_args
4524 )
4525
4526 NOTIFICATION_TYPE_MESSAGE = 'message'
4527 NOTIFICATION_TYPE_NOTICE = 'notice'
4528
4529 NOTIFICATION_LEVEL_INFO = 'info'
4530 NOTIFICATION_LEVEL_WARNING = 'warning'
4531 NOTIFICATION_LEVEL_ERROR = 'error'
4532
4533 user_notice_id = Column('gist_id', Integer(), primary_key=True)
4534
4535 notice_subject = Column('notice_subject', Unicode(512), nullable=True)
4536 notice_body = Column('notice_body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
4537
4538 notice_read = Column('notice_read', Boolean, default=False)
4539
4540 notification_level = Column('notification_level', String(1024), default=NOTIFICATION_LEVEL_INFO)
4541 notification_type = Column('notification_type', String(1024), default=NOTIFICATION_TYPE_NOTICE)
4542
4543 notice_created_by = Column('notice_created_by', Integer(), ForeignKey('users.user_id'), nullable=True)
4544 notice_created_on = Column('notice_created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4545
4546 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'))
4547 user = relationship('User', lazy="joined", primaryjoin='User.user_id==UserNotice.user_id')
4548
4549
4550 class Gist(Base, BaseModel):
4551 __tablename__ = 'gists'
4552 __table_args__ = (
4553 Index('g_gist_access_id_idx', 'gist_access_id'),
4554 Index('g_created_on_idx', 'created_on'),
4555 base_table_args
4556 )
4557
4558 GIST_PUBLIC = u'public'
4559 GIST_PRIVATE = u'private'
4560 DEFAULT_FILENAME = u'gistfile1.txt'
4561
4562 ACL_LEVEL_PUBLIC = u'acl_public'
4563 ACL_LEVEL_PRIVATE = u'acl_private'
4564
4565 gist_id = Column('gist_id', Integer(), primary_key=True)
4566 gist_access_id = Column('gist_access_id', Unicode(250))
4567 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
4568 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
4569 gist_expires = Column('gist_expires', Float(53), nullable=False)
4570 gist_type = Column('gist_type', Unicode(128), nullable=False)
4571 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4572 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4573 acl_level = Column('acl_level', Unicode(128), nullable=True)
4574
4575 owner = relationship('User')
4576
4577 def __repr__(self):
4578 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
4579
4580 @hybrid_property
4581 def description_safe(self):
4582 from rhodecode.lib import helpers as h
4583 return h.escape(self.gist_description)
4584
4585 @classmethod
4586 def get_or_404(cls, id_):
4587 from pyramid.httpexceptions import HTTPNotFound
4588
4589 res = cls.query().filter(cls.gist_access_id == id_).scalar()
4590 if not res:
4591 raise HTTPNotFound()
4592 return res
4593
4594 @classmethod
4595 def get_by_access_id(cls, gist_access_id):
4596 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
4597
4598 def gist_url(self):
4599 from rhodecode.model.gist import GistModel
4600 return GistModel().get_url(self)
4601
4602 @classmethod
4603 def base_path(cls):
4604 """
4605 Returns base path when all gists are stored
4606
4607 :param cls:
4608 """
4609 from rhodecode.model.gist import GIST_STORE_LOC
4610 q = Session().query(RhodeCodeUi)\
4611 .filter(RhodeCodeUi.ui_key == URL_SEP)
4612 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
4613 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
4614
4615 def get_api_data(self):
4616 """
4617 Common function for generating gist related data for API
4618 """
4619 gist = self
4620 data = {
4621 'gist_id': gist.gist_id,
4622 'type': gist.gist_type,
4623 'access_id': gist.gist_access_id,
4624 'description': gist.gist_description,
4625 'url': gist.gist_url(),
4626 'expires': gist.gist_expires,
4627 'created_on': gist.created_on,
4628 'modified_at': gist.modified_at,
4629 'content': None,
4630 'acl_level': gist.acl_level,
4631 }
4632 return data
4633
4634 def __json__(self):
4635 data = dict(
4636 )
4637 data.update(self.get_api_data())
4638 return data
4639 # SCM functions
4640
4641 def scm_instance(self, **kwargs):
4642 """
4643 Get an instance of VCS Repository
4644
4645 :param kwargs:
4646 """
4647 from rhodecode.model.gist import GistModel
4648 full_repo_path = os.path.join(self.base_path(), self.gist_access_id)
4649 return get_vcs_instance(
4650 repo_path=safe_str(full_repo_path), create=False,
4651 _vcs_alias=GistModel.vcs_backend)
4652
4653
4654 class ExternalIdentity(Base, BaseModel):
4655 __tablename__ = 'external_identities'
4656 __table_args__ = (
4657 Index('local_user_id_idx', 'local_user_id'),
4658 Index('external_id_idx', 'external_id'),
4659 base_table_args
4660 )
4661
4662 external_id = Column('external_id', Unicode(255), default=u'', primary_key=True)
4663 external_username = Column('external_username', Unicode(1024), default=u'')
4664 local_user_id = Column('local_user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
4665 provider_name = Column('provider_name', Unicode(255), default=u'', primary_key=True)
4666 access_token = Column('access_token', String(1024), default=u'')
4667 alt_token = Column('alt_token', String(1024), default=u'')
4668 token_secret = Column('token_secret', String(1024), default=u'')
4669
4670 @classmethod
4671 def by_external_id_and_provider(cls, external_id, provider_name, local_user_id=None):
4672 """
4673 Returns ExternalIdentity instance based on search params
4674
4675 :param external_id:
4676 :param provider_name:
4677 :return: ExternalIdentity
4678 """
4679 query = cls.query()
4680 query = query.filter(cls.external_id == external_id)
4681 query = query.filter(cls.provider_name == provider_name)
4682 if local_user_id:
4683 query = query.filter(cls.local_user_id == local_user_id)
4684 return query.first()
4685
4686 @classmethod
4687 def user_by_external_id_and_provider(cls, external_id, provider_name):
4688 """
4689 Returns User instance based on search params
4690
4691 :param external_id:
4692 :param provider_name:
4693 :return: User
4694 """
4695 query = User.query()
4696 query = query.filter(cls.external_id == external_id)
4697 query = query.filter(cls.provider_name == provider_name)
4698 query = query.filter(User.user_id == cls.local_user_id)
4699 return query.first()
4700
4701 @classmethod
4702 def by_local_user_id(cls, local_user_id):
4703 """
4704 Returns all tokens for user
4705
4706 :param local_user_id:
4707 :return: ExternalIdentity
4708 """
4709 query = cls.query()
4710 query = query.filter(cls.local_user_id == local_user_id)
4711 return query
4712
4713 @classmethod
4714 def load_provider_plugin(cls, plugin_id):
4715 from rhodecode.authentication.base import loadplugin
4716 _plugin_id = 'egg:rhodecode-enterprise-ee#{}'.format(plugin_id)
4717 auth_plugin = loadplugin(_plugin_id)
4718 return auth_plugin
4719
4720
4721 class Integration(Base, BaseModel):
4722 __tablename__ = 'integrations'
4723 __table_args__ = (
4724 base_table_args
4725 )
4726
4727 integration_id = Column('integration_id', Integer(), primary_key=True)
4728 integration_type = Column('integration_type', String(255))
4729 enabled = Column('enabled', Boolean(), nullable=False)
4730 name = Column('name', String(255), nullable=False)
4731 child_repos_only = Column('child_repos_only', Boolean(), nullable=False,
4732 default=False)
4733
4734 settings = Column(
4735 'settings_json', MutationObj.as_mutable(
4736 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4737 repo_id = Column(
4738 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
4739 nullable=True, unique=None, default=None)
4740 repo = relationship('Repository', lazy='joined')
4741
4742 repo_group_id = Column(
4743 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
4744 nullable=True, unique=None, default=None)
4745 repo_group = relationship('RepoGroup', lazy='joined')
4746
4747 @property
4748 def scope(self):
4749 if self.repo:
4750 return repr(self.repo)
4751 if self.repo_group:
4752 if self.child_repos_only:
4753 return repr(self.repo_group) + ' (child repos only)'
4754 else:
4755 return repr(self.repo_group) + ' (recursive)'
4756 if self.child_repos_only:
4757 return 'root_repos'
4758 return 'global'
4759
4760 def __repr__(self):
4761 return '<Integration(%r, %r)>' % (self.integration_type, self.scope)
4762
4763
4764 class RepoReviewRuleUser(Base, BaseModel):
4765 __tablename__ = 'repo_review_rules_users'
4766 __table_args__ = (
4767 base_table_args
4768 )
4769
4770 repo_review_rule_user_id = Column('repo_review_rule_user_id', Integer(), primary_key=True)
4771 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
4772 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False)
4773 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4774 user = relationship('User')
4775
4776 def rule_data(self):
4777 return {
4778 'mandatory': self.mandatory
4779 }
4780
4781
4782 class RepoReviewRuleUserGroup(Base, BaseModel):
4783 __tablename__ = 'repo_review_rules_users_groups'
4784 __table_args__ = (
4785 base_table_args
4786 )
4787
4788 VOTE_RULE_ALL = -1
4789
4790 repo_review_rule_users_group_id = Column('repo_review_rule_users_group_id', Integer(), primary_key=True)
4791 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
4792 users_group_id = Column("users_group_id", Integer(),ForeignKey('users_groups.users_group_id'), nullable=False)
4793 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4794 vote_rule = Column("vote_rule", Integer(), nullable=True, default=VOTE_RULE_ALL)
4795 users_group = relationship('UserGroup')
4796
4797 def rule_data(self):
4798 return {
4799 'mandatory': self.mandatory,
4800 'vote_rule': self.vote_rule
4801 }
4802
4803 @property
4804 def vote_rule_label(self):
4805 if not self.vote_rule or self.vote_rule == self.VOTE_RULE_ALL:
4806 return 'all must vote'
4807 else:
4808 return 'min. vote {}'.format(self.vote_rule)
4809
4810
4811 class RepoReviewRule(Base, BaseModel):
4812 __tablename__ = 'repo_review_rules'
4813 __table_args__ = (
4814 base_table_args
4815 )
4816
4817 repo_review_rule_id = Column(
4818 'repo_review_rule_id', Integer(), primary_key=True)
4819 repo_id = Column(
4820 "repo_id", Integer(), ForeignKey('repositories.repo_id'))
4821 repo = relationship('Repository', backref='review_rules')
4822
4823 review_rule_name = Column('review_rule_name', String(255))
4824 _branch_pattern = Column("branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4825 _target_branch_pattern = Column("target_branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4826 _file_pattern = Column("file_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4827
4828 use_authors_for_review = Column("use_authors_for_review", Boolean(), nullable=False, default=False)
4829 forbid_author_to_review = Column("forbid_author_to_review", Boolean(), nullable=False, default=False)
4830 forbid_commit_author_to_review = Column("forbid_commit_author_to_review", Boolean(), nullable=False, default=False)
4831 forbid_adding_reviewers = Column("forbid_adding_reviewers", Boolean(), nullable=False, default=False)
4832
4833 rule_users = relationship('RepoReviewRuleUser')
4834 rule_user_groups = relationship('RepoReviewRuleUserGroup')
4835
4836 def _validate_pattern(self, value):
4837 re.compile('^' + glob2re(value) + '$')
4838
4839 @hybrid_property
4840 def source_branch_pattern(self):
4841 return self._branch_pattern or '*'
4842
4843 @source_branch_pattern.setter
4844 def source_branch_pattern(self, value):
4845 self._validate_pattern(value)
4846 self._branch_pattern = value or '*'
4847
4848 @hybrid_property
4849 def target_branch_pattern(self):
4850 return self._target_branch_pattern or '*'
4851
4852 @target_branch_pattern.setter
4853 def target_branch_pattern(self, value):
4854 self._validate_pattern(value)
4855 self._target_branch_pattern = value or '*'
4856
4857 @hybrid_property
4858 def file_pattern(self):
4859 return self._file_pattern or '*'
4860
4861 @file_pattern.setter
4862 def file_pattern(self, value):
4863 self._validate_pattern(value)
4864 self._file_pattern = value or '*'
4865
4866 def matches(self, source_branch, target_branch, files_changed):
4867 """
4868 Check if this review rule matches a branch/files in a pull request
4869
4870 :param source_branch: source branch name for the commit
4871 :param target_branch: target branch name for the commit
4872 :param files_changed: list of file paths changed in the pull request
4873 """
4874
4875 source_branch = source_branch or ''
4876 target_branch = target_branch or ''
4877 files_changed = files_changed or []
4878
4879 branch_matches = True
4880 if source_branch or target_branch:
4881 if self.source_branch_pattern == '*':
4882 source_branch_match = True
4883 else:
4884 if self.source_branch_pattern.startswith('re:'):
4885 source_pattern = self.source_branch_pattern[3:]
4886 else:
4887 source_pattern = '^' + glob2re(self.source_branch_pattern) + '$'
4888 source_branch_regex = re.compile(source_pattern)
4889 source_branch_match = bool(source_branch_regex.search(source_branch))
4890 if self.target_branch_pattern == '*':
4891 target_branch_match = True
4892 else:
4893 if self.target_branch_pattern.startswith('re:'):
4894 target_pattern = self.target_branch_pattern[3:]
4895 else:
4896 target_pattern = '^' + glob2re(self.target_branch_pattern) + '$'
4897 target_branch_regex = re.compile(target_pattern)
4898 target_branch_match = bool(target_branch_regex.search(target_branch))
4899
4900 branch_matches = source_branch_match and target_branch_match
4901
4902 files_matches = True
4903 if self.file_pattern != '*':
4904 files_matches = False
4905 if self.file_pattern.startswith('re:'):
4906 file_pattern = self.file_pattern[3:]
4907 else:
4908 file_pattern = glob2re(self.file_pattern)
4909 file_regex = re.compile(file_pattern)
4910 for filename in files_changed:
4911 if file_regex.search(filename):
4912 files_matches = True
4913 break
4914
4915 return branch_matches and files_matches
4916
4917 @property
4918 def review_users(self):
4919 """ Returns the users which this rule applies to """
4920
4921 users = collections.OrderedDict()
4922
4923 for rule_user in self.rule_users:
4924 if rule_user.user.active:
4925 if rule_user.user not in users:
4926 users[rule_user.user.username] = {
4927 'user': rule_user.user,
4928 'source': 'user',
4929 'source_data': {},
4930 'data': rule_user.rule_data()
4931 }
4932
4933 for rule_user_group in self.rule_user_groups:
4934 source_data = {
4935 'user_group_id': rule_user_group.users_group.users_group_id,
4936 'name': rule_user_group.users_group.users_group_name,
4937 'members': len(rule_user_group.users_group.members)
4938 }
4939 for member in rule_user_group.users_group.members:
4940 if member.user.active:
4941 key = member.user.username
4942 if key in users:
4943 # skip this member as we have him already
4944 # this prevents from override the "first" matched
4945 # users with duplicates in multiple groups
4946 continue
4947
4948 users[key] = {
4949 'user': member.user,
4950 'source': 'user_group',
4951 'source_data': source_data,
4952 'data': rule_user_group.rule_data()
4953 }
4954
4955 return users
4956
4957 def user_group_vote_rule(self, user_id):
4958
4959 rules = []
4960 if not self.rule_user_groups:
4961 return rules
4962
4963 for user_group in self.rule_user_groups:
4964 user_group_members = [x.user_id for x in user_group.users_group.members]
4965 if user_id in user_group_members:
4966 rules.append(user_group)
4967 return rules
4968
4969 def __repr__(self):
4970 return '<RepoReviewerRule(id=%r, repo=%r)>' % (
4971 self.repo_review_rule_id, self.repo)
4972
4973
4974 class ScheduleEntry(Base, BaseModel):
4975 __tablename__ = 'schedule_entries'
4976 __table_args__ = (
4977 UniqueConstraint('schedule_name', name='s_schedule_name_idx'),
4978 UniqueConstraint('task_uid', name='s_task_uid_idx'),
4979 base_table_args,
4980 )
4981
4982 schedule_types = ['crontab', 'timedelta', 'integer']
4983 schedule_entry_id = Column('schedule_entry_id', Integer(), primary_key=True)
4984
4985 schedule_name = Column("schedule_name", String(255), nullable=False, unique=None, default=None)
4986 schedule_description = Column("schedule_description", String(10000), nullable=True, unique=None, default=None)
4987 schedule_enabled = Column("schedule_enabled", Boolean(), nullable=False, unique=None, default=True)
4988
4989 _schedule_type = Column("schedule_type", String(255), nullable=False, unique=None, default=None)
4990 schedule_definition = Column('schedule_definition_json', MutationObj.as_mutable(JsonType(default=lambda: "", dialect_map=dict(mysql=LONGTEXT()))))
4991
4992 schedule_last_run = Column('schedule_last_run', DateTime(timezone=False), nullable=True, unique=None, default=None)
4993 schedule_total_run_count = Column('schedule_total_run_count', Integer(), nullable=True, unique=None, default=0)
4994
4995 # task
4996 task_uid = Column("task_uid", String(255), nullable=False, unique=None, default=None)
4997 task_dot_notation = Column("task_dot_notation", String(4096), nullable=False, unique=None, default=None)
4998 task_args = Column('task_args_json', MutationObj.as_mutable(JsonType(default=list, dialect_map=dict(mysql=LONGTEXT()))))
4999 task_kwargs = Column('task_kwargs_json', MutationObj.as_mutable(JsonType(default=dict, dialect_map=dict(mysql=LONGTEXT()))))
5000
5001 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
5002 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=None)
5003
5004 @hybrid_property
5005 def schedule_type(self):
5006 return self._schedule_type
5007
5008 @schedule_type.setter
5009 def schedule_type(self, val):
5010 if val not in self.schedule_types:
5011 raise ValueError('Value must be on of `{}` and got `{}`'.format(
5012 val, self.schedule_type))
5013
5014 self._schedule_type = val
5015
5016 @classmethod
5017 def get_uid(cls, obj):
5018 args = obj.task_args
5019 kwargs = obj.task_kwargs
5020 if isinstance(args, JsonRaw):
5021 try:
5022 args = json.loads(args)
5023 except ValueError:
5024 args = tuple()
5025
5026 if isinstance(kwargs, JsonRaw):
5027 try:
5028 kwargs = json.loads(kwargs)
5029 except ValueError:
5030 kwargs = dict()
5031
5032 dot_notation = obj.task_dot_notation
5033 val = '.'.join(map(safe_str, [
5034 sorted(dot_notation), args, sorted(kwargs.items())]))
5035 return hashlib.sha1(val).hexdigest()
5036
5037 @classmethod
5038 def get_by_schedule_name(cls, schedule_name):
5039 return cls.query().filter(cls.schedule_name == schedule_name).scalar()
5040
5041 @classmethod
5042 def get_by_schedule_id(cls, schedule_id):
5043 return cls.query().filter(cls.schedule_entry_id == schedule_id).scalar()
5044
5045 @property
5046 def task(self):
5047 return self.task_dot_notation
5048
5049 @property
5050 def schedule(self):
5051 from rhodecode.lib.celerylib.utils import raw_2_schedule
5052 schedule = raw_2_schedule(self.schedule_definition, self.schedule_type)
5053 return schedule
5054
5055 @property
5056 def args(self):
5057 try:
5058 return list(self.task_args or [])
5059 except ValueError:
5060 return list()
5061
5062 @property
5063 def kwargs(self):
5064 try:
5065 return dict(self.task_kwargs or {})
5066 except ValueError:
5067 return dict()
5068
5069 def _as_raw(self, val):
5070 if hasattr(val, 'de_coerce'):
5071 val = val.de_coerce()
5072 if val:
5073 val = json.dumps(val)
5074
5075 return val
5076
5077 @property
5078 def schedule_definition_raw(self):
5079 return self._as_raw(self.schedule_definition)
5080
5081 @property
5082 def args_raw(self):
5083 return self._as_raw(self.task_args)
5084
5085 @property
5086 def kwargs_raw(self):
5087 return self._as_raw(self.task_kwargs)
5088
5089 def __repr__(self):
5090 return '<DB:ScheduleEntry({}:{})>'.format(
5091 self.schedule_entry_id, self.schedule_name)
5092
5093
5094 @event.listens_for(ScheduleEntry, 'before_update')
5095 def update_task_uid(mapper, connection, target):
5096 target.task_uid = ScheduleEntry.get_uid(target)
5097
5098
5099 @event.listens_for(ScheduleEntry, 'before_insert')
5100 def set_task_uid(mapper, connection, target):
5101 target.task_uid = ScheduleEntry.get_uid(target)
5102
5103
5104 class _BaseBranchPerms(BaseModel):
5105 @classmethod
5106 def compute_hash(cls, value):
5107 return sha1_safe(value)
5108
5109 @hybrid_property
5110 def branch_pattern(self):
5111 return self._branch_pattern or '*'
5112
5113 @hybrid_property
5114 def branch_hash(self):
5115 return self._branch_hash
5116
5117 def _validate_glob(self, value):
5118 re.compile('^' + glob2re(value) + '$')
5119
5120 @branch_pattern.setter
5121 def branch_pattern(self, value):
5122 self._validate_glob(value)
5123 self._branch_pattern = value or '*'
5124 # set the Hash when setting the branch pattern
5125 self._branch_hash = self.compute_hash(self._branch_pattern)
5126
5127 def matches(self, branch):
5128 """
5129 Check if this the branch matches entry
5130
5131 :param branch: branch name for the commit
5132 """
5133
5134 branch = branch or ''
5135
5136 branch_matches = True
5137 if branch:
5138 branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$')
5139 branch_matches = bool(branch_regex.search(branch))
5140
5141 return branch_matches
5142
5143
5144 class UserToRepoBranchPermission(Base, _BaseBranchPerms):
5145 __tablename__ = 'user_to_repo_branch_permissions'
5146 __table_args__ = (
5147 base_table_args
5148 )
5149
5150 branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
5151
5152 repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
5153 repo = relationship('Repository', backref='user_branch_perms')
5154
5155 permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
5156 permission = relationship('Permission')
5157
5158 rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('repo_to_perm.repo_to_perm_id'), nullable=False, unique=None, default=None)
5159 user_repo_to_perm = relationship('UserRepoToPerm')
5160
5161 rule_order = Column('rule_order', Integer(), nullable=False)
5162 _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
5163 _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
5164
5165 def __unicode__(self):
5166 return u'<UserBranchPermission(%s => %r)>' % (
5167 self.user_repo_to_perm, self.branch_pattern)
5168
5169
5170 class UserGroupToRepoBranchPermission(Base, _BaseBranchPerms):
5171 __tablename__ = 'user_group_to_repo_branch_permissions'
5172 __table_args__ = (
5173 base_table_args
5174 )
5175
5176 branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
5177
5178 repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
5179 repo = relationship('Repository', backref='user_group_branch_perms')
5180
5181 permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
5182 permission = relationship('Permission')
5183
5184 rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('users_group_repo_to_perm.users_group_to_perm_id'), nullable=False, unique=None, default=None)
5185 user_group_repo_to_perm = relationship('UserGroupRepoToPerm')
5186
5187 rule_order = Column('rule_order', Integer(), nullable=False)
5188 _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
5189 _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
5190
5191 def __unicode__(self):
5192 return u'<UserBranchPermission(%s => %r)>' % (
5193 self.user_group_repo_to_perm, self.branch_pattern)
5194
5195
5196 class UserBookmark(Base, BaseModel):
5197 __tablename__ = 'user_bookmarks'
5198 __table_args__ = (
5199 UniqueConstraint('user_id', 'bookmark_repo_id'),
5200 UniqueConstraint('user_id', 'bookmark_repo_group_id'),
5201 UniqueConstraint('user_id', 'bookmark_position'),
5202 base_table_args
5203 )
5204
5205 user_bookmark_id = Column("user_bookmark_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
5206 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
5207 position = Column("bookmark_position", Integer(), nullable=False)
5208 title = Column("bookmark_title", String(255), nullable=True, unique=None, default=None)
5209 redirect_url = Column("bookmark_redirect_url", String(10240), nullable=True, unique=None, default=None)
5210 created_on = Column("created_on", DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
5211
5212 bookmark_repo_id = Column("bookmark_repo_id", Integer(), ForeignKey("repositories.repo_id"), nullable=True, unique=None, default=None)
5213 bookmark_repo_group_id = Column("bookmark_repo_group_id", Integer(), ForeignKey("groups.group_id"), nullable=True, unique=None, default=None)
5214
5215 user = relationship("User")
5216
5217 repository = relationship("Repository")
5218 repository_group = relationship("RepoGroup")
5219
5220 @classmethod
5221 def get_by_position_for_user(cls, position, user_id):
5222 return cls.query() \
5223 .filter(UserBookmark.user_id == user_id) \
5224 .filter(UserBookmark.position == position).scalar()
5225
5226 @classmethod
5227 def get_bookmarks_for_user(cls, user_id, cache=True):
5228 bookmarks = cls.query() \
5229 .filter(UserBookmark.user_id == user_id) \
5230 .options(joinedload(UserBookmark.repository)) \
5231 .options(joinedload(UserBookmark.repository_group)) \
5232 .order_by(UserBookmark.position.asc())
5233
5234 if cache:
5235 bookmarks = bookmarks.options(
5236 FromCache("sql_cache_short", "get_user_{}_bookmarks".format(user_id))
5237 )
5238
5239 return bookmarks.all()
5240
5241 def __unicode__(self):
5242 return u'<UserBookmark(%s @ %r)>' % (self.position, self.redirect_url)
5243
5244
5245 class FileStore(Base, BaseModel):
5246 __tablename__ = 'file_store'
5247 __table_args__ = (
5248 base_table_args
5249 )
5250
5251 file_store_id = Column('file_store_id', Integer(), primary_key=True)
5252 file_uid = Column('file_uid', String(1024), nullable=False)
5253 file_display_name = Column('file_display_name', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), nullable=True)
5254 file_description = Column('file_description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
5255 file_org_name = Column('file_org_name', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=False)
5256
5257 # sha256 hash
5258 file_hash = Column('file_hash', String(512), nullable=False)
5259 file_size = Column('file_size', BigInteger(), nullable=False)
5260
5261 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
5262 accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True)
5263 accessed_count = Column('accessed_count', Integer(), default=0)
5264
5265 enabled = Column('enabled', Boolean(), nullable=False, default=True)
5266
5267 # if repo/repo_group reference is set, check for permissions
5268 check_acl = Column('check_acl', Boolean(), nullable=False, default=True)
5269
5270 # hidden defines an attachment that should be hidden from showing in artifact listing
5271 hidden = Column('hidden', Boolean(), nullable=False, default=False)
5272
5273 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
5274 upload_user = relationship('User', lazy='joined', primaryjoin='User.user_id==FileStore.user_id')
5275
5276 file_metadata = relationship('FileStoreMetadata', lazy='joined')
5277
5278 # scope limited to user, which requester have access to
5279 scope_user_id = Column(
5280 'scope_user_id', Integer(), ForeignKey('users.user_id'),
5281 nullable=True, unique=None, default=None)
5282 user = relationship('User', lazy='joined', primaryjoin='User.user_id==FileStore.scope_user_id')
5283
5284 # scope limited to user group, which requester have access to
5285 scope_user_group_id = Column(
5286 'scope_user_group_id', Integer(), ForeignKey('users_groups.users_group_id'),
5287 nullable=True, unique=None, default=None)
5288 user_group = relationship('UserGroup', lazy='joined')
5289
5290 # scope limited to repo, which requester have access to
5291 scope_repo_id = Column(
5292 'scope_repo_id', Integer(), ForeignKey('repositories.repo_id'),
5293 nullable=True, unique=None, default=None)
5294 repo = relationship('Repository', lazy='joined')
5295
5296 # scope limited to repo group, which requester have access to
5297 scope_repo_group_id = Column(
5298 'scope_repo_group_id', Integer(), ForeignKey('groups.group_id'),
5299 nullable=True, unique=None, default=None)
5300 repo_group = relationship('RepoGroup', lazy='joined')
5301
5302 @classmethod
5303 def get_by_store_uid(cls, file_store_uid):
5304 return FileStore.query().filter(FileStore.file_uid == file_store_uid).scalar()
5305
5306 @classmethod
5307 def create(cls, file_uid, filename, file_hash, file_size, file_display_name='',
5308 file_description='', enabled=True, hidden=False, check_acl=True,
5309 user_id=None, scope_user_id=None, scope_repo_id=None, scope_repo_group_id=None):
5310
5311 store_entry = FileStore()
5312 store_entry.file_uid = file_uid
5313 store_entry.file_display_name = file_display_name
5314 store_entry.file_org_name = filename
5315 store_entry.file_size = file_size
5316 store_entry.file_hash = file_hash
5317 store_entry.file_description = file_description
5318
5319 store_entry.check_acl = check_acl
5320 store_entry.enabled = enabled
5321 store_entry.hidden = hidden
5322
5323 store_entry.user_id = user_id
5324 store_entry.scope_user_id = scope_user_id
5325 store_entry.scope_repo_id = scope_repo_id
5326 store_entry.scope_repo_group_id = scope_repo_group_id
5327
5328 return store_entry
5329
5330 @classmethod
5331 def store_metadata(cls, file_store_id, args, commit=True):
5332 file_store = FileStore.get(file_store_id)
5333 if file_store is None:
5334 return
5335
5336 for section, key, value, value_type in args:
5337 has_key = FileStoreMetadata().query() \
5338 .filter(FileStoreMetadata.file_store_id == file_store.file_store_id) \
5339 .filter(FileStoreMetadata.file_store_meta_section == section) \
5340 .filter(FileStoreMetadata.file_store_meta_key == key) \
5341 .scalar()
5342 if has_key:
5343 msg = 'key `{}` already defined under section `{}` for this file.'\
5344 .format(key, section)
5345 raise ArtifactMetadataDuplicate(msg, err_section=section, err_key=key)
5346
5347 # NOTE(marcink): raises ArtifactMetadataBadValueType
5348 FileStoreMetadata.valid_value_type(value_type)
5349
5350 meta_entry = FileStoreMetadata()
5351 meta_entry.file_store = file_store
5352 meta_entry.file_store_meta_section = section
5353 meta_entry.file_store_meta_key = key
5354 meta_entry.file_store_meta_value_type = value_type
5355 meta_entry.file_store_meta_value = value
5356
5357 Session().add(meta_entry)
5358
5359 try:
5360 if commit:
5361 Session().commit()
5362 except IntegrityError:
5363 Session().rollback()
5364 raise ArtifactMetadataDuplicate('Duplicate section/key found for this file.')
5365
5366 @classmethod
5367 def bump_access_counter(cls, file_uid, commit=True):
5368 FileStore().query()\
5369 .filter(FileStore.file_uid == file_uid)\
5370 .update({FileStore.accessed_count: (FileStore.accessed_count + 1),
5371 FileStore.accessed_on: datetime.datetime.now()})
5372 if commit:
5373 Session().commit()
5374
5375 def __json__(self):
5376 data = {
5377 'filename': self.file_display_name,
5378 'filename_org': self.file_org_name,
5379 'file_uid': self.file_uid,
5380 'description': self.file_description,
5381 'hidden': self.hidden,
5382 'size': self.file_size,
5383 'created_on': self.created_on,
5384 'uploaded_by': self.upload_user.get_api_data(details='basic'),
5385 'downloaded_times': self.accessed_count,
5386 'sha256': self.file_hash,
5387 'metadata': self.file_metadata,
5388 }
5389
5390 return data
5391
5392 def __repr__(self):
5393 return '<FileStore({})>'.format(self.file_store_id)
5394
5395
5396 class FileStoreMetadata(Base, BaseModel):
5397 __tablename__ = 'file_store_metadata'
5398 __table_args__ = (
5399 UniqueConstraint('file_store_id', 'file_store_meta_section_hash', 'file_store_meta_key_hash'),
5400 Index('file_store_meta_section_idx', 'file_store_meta_section', mysql_length=255),
5401 Index('file_store_meta_key_idx', 'file_store_meta_key', mysql_length=255),
5402 base_table_args
5403 )
5404 SETTINGS_TYPES = {
5405 'str': safe_str,
5406 'int': safe_int,
5407 'unicode': safe_unicode,
5408 'bool': str2bool,
5409 'list': functools.partial(aslist, sep=',')
5410 }
5411
5412 file_store_meta_id = Column(
5413 "file_store_meta_id", Integer(), nullable=False, unique=True, default=None,
5414 primary_key=True)
5415 _file_store_meta_section = Column(
5416 "file_store_meta_section", UnicodeText().with_variant(UnicodeText(1024), 'mysql'),
5417 nullable=True, unique=None, default=None)
5418 _file_store_meta_section_hash = Column(
5419 "file_store_meta_section_hash", String(255),
5420 nullable=True, unique=None, default=None)
5421 _file_store_meta_key = Column(
5422 "file_store_meta_key", UnicodeText().with_variant(UnicodeText(1024), 'mysql'),
5423 nullable=True, unique=None, default=None)
5424 _file_store_meta_key_hash = Column(
5425 "file_store_meta_key_hash", String(255), nullable=True, unique=None, default=None)
5426 _file_store_meta_value = Column(
5427 "file_store_meta_value", UnicodeText().with_variant(UnicodeText(20480), 'mysql'),
5428 nullable=True, unique=None, default=None)
5429 _file_store_meta_value_type = Column(
5430 "file_store_meta_value_type", String(255), nullable=True, unique=None,
5431 default='unicode')
5432
5433 file_store_id = Column(
5434 'file_store_id', Integer(), ForeignKey('file_store.file_store_id'),
5435 nullable=True, unique=None, default=None)
5436
5437 file_store = relationship('FileStore', lazy='joined')
5438
5439 @classmethod
5440 def valid_value_type(cls, value):
5441 if value.split('.')[0] not in cls.SETTINGS_TYPES:
5442 raise ArtifactMetadataBadValueType(
5443 'value_type must be one of %s got %s' % (cls.SETTINGS_TYPES.keys(), value))
5444
5445 @hybrid_property
5446 def file_store_meta_section(self):
5447 return self._file_store_meta_section
5448
5449 @file_store_meta_section.setter
5450 def file_store_meta_section(self, value):
5451 self._file_store_meta_section = value
5452 self._file_store_meta_section_hash = _hash_key(value)
5453
5454 @hybrid_property
5455 def file_store_meta_key(self):
5456 return self._file_store_meta_key
5457
5458 @file_store_meta_key.setter
5459 def file_store_meta_key(self, value):
5460 self._file_store_meta_key = value
5461 self._file_store_meta_key_hash = _hash_key(value)
5462
5463 @hybrid_property
5464 def file_store_meta_value(self):
5465 val = self._file_store_meta_value
5466
5467 if self._file_store_meta_value_type:
5468 # e.g unicode.encrypted == unicode
5469 _type = self._file_store_meta_value_type.split('.')[0]
5470 # decode the encrypted value if it's encrypted field type
5471 if '.encrypted' in self._file_store_meta_value_type:
5472 cipher = EncryptedTextValue()
5473 val = safe_unicode(cipher.process_result_value(val, None))
5474 # do final type conversion
5475 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
5476 val = converter(val)
5477
5478 return val
5479
5480 @file_store_meta_value.setter
5481 def file_store_meta_value(self, val):
5482 val = safe_unicode(val)
5483 # encode the encrypted value
5484 if '.encrypted' in self.file_store_meta_value_type:
5485 cipher = EncryptedTextValue()
5486 val = safe_unicode(cipher.process_bind_param(val, None))
5487 self._file_store_meta_value = val
5488
5489 @hybrid_property
5490 def file_store_meta_value_type(self):
5491 return self._file_store_meta_value_type
5492
5493 @file_store_meta_value_type.setter
5494 def file_store_meta_value_type(self, val):
5495 # e.g unicode.encrypted
5496 self.valid_value_type(val)
5497 self._file_store_meta_value_type = val
5498
5499 def __json__(self):
5500 data = {
5501 'artifact': self.file_store.file_uid,
5502 'section': self.file_store_meta_section,
5503 'key': self.file_store_meta_key,
5504 'value': self.file_store_meta_value,
5505 }
5506
5507 return data
5508
5509 def __repr__(self):
5510 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.file_store_meta_section,
5511 self.file_store_meta_key, self.file_store_meta_value)
5512
5513
5514 class DbMigrateVersion(Base, BaseModel):
5515 __tablename__ = 'db_migrate_version'
5516 __table_args__ = (
5517 base_table_args,
5518 )
5519
5520 repository_id = Column('repository_id', String(250), primary_key=True)
5521 repository_path = Column('repository_path', Text)
5522 version = Column('version', Integer)
5523
5524 @classmethod
5525 def set_version(cls, version):
5526 """
5527 Helper for forcing a different version, usually for debugging purposes via ishell.
5528 """
5529 ver = DbMigrateVersion.query().first()
5530 ver.version = version
5531 Session().commit()
5532
5533
5534 class DbSession(Base, BaseModel):
5535 __tablename__ = 'db_session'
5536 __table_args__ = (
5537 base_table_args,
5538 )
5539
5540 def __repr__(self):
5541 return '<DB:DbSession({})>'.format(self.id)
5542
5543 id = Column('id', Integer())
5544 namespace = Column('namespace', String(255), primary_key=True)
5545 accessed = Column('accessed', DateTime, nullable=False)
5546 created = Column('created', DateTime, nullable=False)
5547 data = Column('data', PickleType, nullable=False)
@@ -0,0 +1,52 b''
1 # -*- coding: utf-8 -*-
2
3 import logging
4 from sqlalchemy import *
5
6 from alembic.migration import MigrationContext
7 from alembic.operations import Operations
8 from sqlalchemy import BigInteger
9
10 from rhodecode.lib.dbmigrate.versions import _reset_base
11 from rhodecode.model import init_model_encryption
12
13
14 log = logging.getLogger(__name__)
15
16
17 def upgrade(migrate_engine):
18 """
19 Upgrade operations go here.
20 Don't create your own engine; bind migrate_engine to your metadata
21 """
22 _reset_base(migrate_engine)
23 from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db
24
25 init_model_encryption(db)
26
27 context = MigrationContext.configure(migrate_engine.connect())
28 op = Operations(context)
29
30 pull_requests = db.PullRequest.__table__
31
32 with op.batch_alter_table(pull_requests.name) as batch_op:
33 new_column = Column(
34 'last_merge_metadata',
35 db.JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
36 batch_op.add_column(new_column)
37
38 pull_request_version = db.PullRequestVersion.__table__
39 with op.batch_alter_table(pull_request_version.name) as batch_op:
40 new_column = Column(
41 'last_merge_metadata',
42 db.JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
43 batch_op.add_column(new_column)
44
45
46 def downgrade(migrate_engine):
47 meta = MetaData()
48 meta.bind = migrate_engine
49
50
51 def fixups(models, _SESSION):
52 pass
@@ -0,0 +1,35 b''
1 # -*- coding: utf-8 -*-
2
3 import logging
4 from sqlalchemy import *
5
6 from alembic.migration import MigrationContext
7 from alembic.operations import Operations
8 from sqlalchemy import BigInteger
9
10 from rhodecode.lib.dbmigrate.versions import _reset_base
11 from rhodecode.model import init_model_encryption
12
13
14 log = logging.getLogger(__name__)
15
16
17 def upgrade(migrate_engine):
18 """
19 Upgrade operations go here.
20 Don't create your own engine; bind migrate_engine to your metadata
21 """
22 _reset_base(migrate_engine)
23 from rhodecode.lib.dbmigrate.schema import db_4_19_0_0 as db
24
25 init_model_encryption(db)
26 db.UserNotice().__table__.create()
27
28
29 def downgrade(migrate_engine):
30 meta = MetaData()
31 meta.bind = migrate_engine
32
33
34 def fixups(models, _SESSION):
35 pass
@@ -0,0 +1,43 b''
1 # -*- coding: utf-8 -*-
2
3 import logging
4 from sqlalchemy import *
5
6 from alembic.migration import MigrationContext
7 from alembic.operations import Operations
8 from sqlalchemy import BigInteger
9
10 from rhodecode.lib.dbmigrate.versions import _reset_base
11 from rhodecode.model import init_model_encryption
12
13
14 log = logging.getLogger(__name__)
15
16
17 def upgrade(migrate_engine):
18 """
19 Upgrade operations go here.
20 Don't create your own engine; bind migrate_engine to your metadata
21 """
22 _reset_base(migrate_engine)
23 from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db
24
25 init_model_encryption(db)
26
27 context = MigrationContext.configure(migrate_engine.connect())
28 op = Operations(context)
29
30 comments = db.ChangesetComment.__table__
31
32 with op.batch_alter_table(comments.name) as batch_op:
33 new_column = Column('immutable_state', Unicode(128), nullable=True)
34 batch_op.add_column(new_column)
35
36
37 def downgrade(migrate_engine):
38 meta = MetaData()
39 meta.bind = migrate_engine
40
41
42 def fixups(models, _SESSION):
43 pass
@@ -0,0 +1,47 b''
1 # -*- coding: utf-8 -*-
2
3 import logging
4 from sqlalchemy import *
5
6 from alembic.migration import MigrationContext
7 from alembic.operations import Operations
8 from sqlalchemy import BigInteger
9
10 from rhodecode.lib.dbmigrate.versions import _reset_base
11 from rhodecode.model import init_model_encryption
12
13
14 log = logging.getLogger(__name__)
15
16
17 def upgrade(migrate_engine):
18 """
19 Upgrade operations go here.
20 Don't create your own engine; bind migrate_engine to your metadata
21 """
22 _reset_base(migrate_engine)
23 from rhodecode.lib.dbmigrate.schema import db_4_19_0_0 as db
24
25 init_model_encryption(db)
26
27 context = MigrationContext.configure(migrate_engine.connect())
28 op = Operations(context)
29
30 pull_requests = db.PullRequest.__table__
31 with op.batch_alter_table(pull_requests.name) as batch_op:
32 new_column = Column('common_ancestor_id', Unicode(255), nullable=True)
33 batch_op.add_column(new_column)
34
35 pull_request_version = db.PullRequestVersion.__table__
36 with op.batch_alter_table(pull_request_version.name) as batch_op:
37 new_column = Column('common_ancestor_id', Unicode(255), nullable=True)
38 batch_op.add_column(new_column)
39
40
41 def downgrade(migrate_engine):
42 meta = MetaData()
43 meta.bind = migrate_engine
44
45
46 def fixups(models, _SESSION):
47 pass
This diff has been collapsed as it changes many lines, (1544 lines changed) Show them Hide them
@@ -0,0 +1,1544 b''
1 .swal2-popup.swal2-toast {
2 flex-direction: row;
3 align-items: center;
4 width: auto;
5 padding: 0.625em;
6 overflow-y: hidden;
7 background: #fff;
8 box-shadow: none;
9 }
10
11 .swal2-popup.swal2-toast .swal2-header {
12 flex-direction: row;
13 }
14
15 .swal2-popup.swal2-toast .swal2-title {
16 flex-grow: 1;
17 justify-content: flex-start;
18 margin: 0 0.6em;
19 font-size: 1em;
20 }
21
22 .swal2-popup.swal2-toast .swal2-footer {
23 margin: 0.5em 0 0;
24 padding: 0.5em 0 0;
25 font-size: 0.8em;
26 }
27
28 .swal2-popup.swal2-toast .swal2-close {
29 position: static;
30 width: 0.8em;
31 height: 0.8em;
32 line-height: 0.8;
33 }
34
35 .swal2-popup.swal2-toast .swal2-content {
36 justify-content: flex-start;
37 font-size: 1em;
38 }
39
40 .swal2-popup.swal2-toast .swal2-icon {
41 width: 2em;
42 min-width: 2em;
43 height: 2em;
44 margin: 0;
45 }
46
47 .swal2-popup.swal2-toast .swal2-icon .swal2-icon-content {
48 display: flex;
49 align-items: center;
50 font-size: 1.8em;
51 font-weight: bold;
52 }
53
54 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
55 .swal2-popup.swal2-toast .swal2-icon .swal2-icon-content {
56 font-size: 0.25em;
57 }
58 }
59
60 .swal2-popup.swal2-toast .swal2-icon.swal2-success .swal2-success-ring {
61 width: 2em;
62 height: 2em;
63 }
64
65 .swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line] {
66 top: 0.875em;
67 width: 1.375em;
68 }
69
70 .swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left] {
71 left: 0.3125em;
72 }
73
74 .swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right] {
75 right: 0.3125em;
76 }
77
78 .swal2-popup.swal2-toast .swal2-actions {
79 flex-basis: auto !important;
80 width: auto;
81 height: auto;
82 margin: 0 0.3125em;
83 }
84
85 .swal2-popup.swal2-toast .swal2-styled {
86 margin: 0 0.3125em;
87 padding: 0.3125em 0.625em;
88 font-size: 1em;
89 }
90
91 .swal2-popup.swal2-toast .swal2-styled:focus {
92 box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(50, 100, 150, 0.4);
93 }
94
95 .swal2-popup.swal2-toast .swal2-success {
96 border-color: @alert1;
97 }
98
99 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line] {
100 position: absolute;
101 width: 1.6em;
102 height: 3em;
103 transform: rotate(45deg);
104 border-radius: 50%;
105 }
106
107 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left] {
108 top: -0.8em;
109 left: -0.5em;
110 transform: rotate(-45deg);
111 transform-origin: 2em 2em;
112 border-radius: 4em 0 0 4em;
113 }
114
115 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right] {
116 top: -0.25em;
117 left: 0.9375em;
118 transform-origin: 0 1.5em;
119 border-radius: 0 4em 4em 0;
120 }
121
122 .swal2-popup.swal2-toast .swal2-success .swal2-success-ring {
123 width: 2em;
124 height: 2em;
125 }
126
127 .swal2-popup.swal2-toast .swal2-success .swal2-success-fix {
128 top: 0;
129 left: 0.4375em;
130 width: 0.4375em;
131 height: 2.6875em;
132 }
133
134 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line] {
135 height: 0.3125em;
136 }
137
138 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip] {
139 top: 1.125em;
140 left: 0.1875em;
141 width: 0.75em;
142 }
143
144 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=long] {
145 top: 0.9375em;
146 right: 0.1875em;
147 width: 1.375em;
148 }
149
150 .swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip {
151 -webkit-animation: swal2-toast-animate-success-line-tip 0.75s;
152 animation: swal2-toast-animate-success-line-tip 0.75s;
153 }
154
155 .swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long {
156 -webkit-animation: swal2-toast-animate-success-line-long 0.75s;
157 animation: swal2-toast-animate-success-line-long 0.75s;
158 }
159
160 .swal2-popup.swal2-toast.swal2-show {
161 -webkit-animation: swal2-toast-show 0.5s;
162 animation: swal2-toast-show 0.5s;
163 }
164
165 .swal2-popup.swal2-toast.swal2-hide {
166 -webkit-animation: swal2-toast-hide 0.1s forwards;
167 animation: swal2-toast-hide 0.1s forwards;
168 }
169
170 .swal2-container {
171 display: flex;
172 position: fixed;
173 z-index: 1060;
174 top: 0;
175 right: 0;
176 bottom: 0;
177 left: 0;
178 flex-direction: row;
179 align-items: center;
180 justify-content: center;
181 padding: 0.625em;
182 overflow-x: hidden;
183 transition: background-color 0.1s;
184 -webkit-overflow-scrolling: touch;
185 }
186
187 .swal2-container.swal2-backdrop-show, .swal2-container.swal2-noanimation {
188 background: rgba(0, 0, 0, 0.4);
189 }
190
191 .swal2-container.swal2-backdrop-hide {
192 background: transparent !important;
193 }
194
195 .swal2-container.swal2-top {
196 align-items: flex-start;
197 }
198
199 .swal2-container.swal2-top-start, .swal2-container.swal2-top-left {
200 align-items: flex-start;
201 justify-content: flex-start;
202 }
203
204 .swal2-container.swal2-top-end, .swal2-container.swal2-top-right {
205 align-items: flex-start;
206 justify-content: flex-end;
207 }
208
209 .swal2-container.swal2-center {
210 align-items: center;
211 }
212
213 .swal2-container.swal2-center-start, .swal2-container.swal2-center-left {
214 align-items: center;
215 justify-content: flex-start;
216 }
217
218 .swal2-container.swal2-center-end, .swal2-container.swal2-center-right {
219 align-items: center;
220 justify-content: flex-end;
221 }
222
223 .swal2-container.swal2-bottom {
224 align-items: flex-end;
225 }
226
227 .swal2-container.swal2-bottom-start, .swal2-container.swal2-bottom-left {
228 align-items: flex-end;
229 justify-content: flex-start;
230 }
231
232 .swal2-container.swal2-bottom-end, .swal2-container.swal2-bottom-right {
233 align-items: flex-end;
234 justify-content: flex-end;
235 }
236
237 .swal2-container.swal2-bottom > :first-child,
238 .swal2-container.swal2-bottom-start > :first-child,
239 .swal2-container.swal2-bottom-left > :first-child,
240 .swal2-container.swal2-bottom-end > :first-child,
241 .swal2-container.swal2-bottom-right > :first-child {
242 margin-top: auto;
243 }
244
245 .swal2-container.swal2-grow-fullscreen > .swal2-modal {
246 display: flex !important;
247 flex: 1;
248 align-self: stretch;
249 justify-content: center;
250 }
251
252 .swal2-container.swal2-grow-row > .swal2-modal {
253 display: flex !important;
254 flex: 1;
255 align-content: center;
256 justify-content: center;
257 }
258
259 .swal2-container.swal2-grow-column {
260 flex: 1;
261 flex-direction: column;
262 }
263
264 .swal2-container.swal2-grow-column.swal2-top, .swal2-container.swal2-grow-column.swal2-center, .swal2-container.swal2-grow-column.swal2-bottom {
265 align-items: center;
266 }
267
268 .swal2-container.swal2-grow-column.swal2-top-start, .swal2-container.swal2-grow-column.swal2-center-start, .swal2-container.swal2-grow-column.swal2-bottom-start, .swal2-container.swal2-grow-column.swal2-top-left, .swal2-container.swal2-grow-column.swal2-center-left, .swal2-container.swal2-grow-column.swal2-bottom-left {
269 align-items: flex-start;
270 }
271
272 .swal2-container.swal2-grow-column.swal2-top-end, .swal2-container.swal2-grow-column.swal2-center-end, .swal2-container.swal2-grow-column.swal2-bottom-end, .swal2-container.swal2-grow-column.swal2-top-right, .swal2-container.swal2-grow-column.swal2-center-right, .swal2-container.swal2-grow-column.swal2-bottom-right {
273 align-items: flex-end;
274 }
275
276 .swal2-container.swal2-grow-column > .swal2-modal {
277 display: flex !important;
278 flex: 1;
279 align-content: center;
280 justify-content: center;
281 }
282
283 .swal2-container.swal2-no-transition {
284 transition: none !important;
285 }
286
287 .swal2-container:not(.swal2-top):not(.swal2-top-start):not(.swal2-top-end):not(.swal2-top-left):not(.swal2-top-right):not(.swal2-center-start):not(.swal2-center-end):not(.swal2-center-left):not(.swal2-center-right):not(.swal2-bottom):not(.swal2-bottom-start):not(.swal2-bottom-end):not(.swal2-bottom-left):not(.swal2-bottom-right):not(.swal2-grow-fullscreen) > .swal2-modal {
288 margin: auto;
289 }
290
291 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
292 .swal2-container .swal2-modal {
293 margin: 0 !important;
294 }
295 }
296
297 .swal2-popup {
298 display: none;
299 position: relative;
300 box-sizing: border-box;
301 flex-direction: column;
302 justify-content: center;
303 width: 38em;
304 max-width: 100%;
305 padding: 1.25em;
306 border: none;
307 border-radius: 0;
308 background: #fff;
309 font-family: inherit;
310 font-size: 1rem;
311 }
312
313 .swal2-popup:focus {
314 outline: none;
315 }
316
317 .swal2-popup.swal2-loading {
318 overflow-y: hidden;
319 }
320
321 .swal2-header {
322 display: flex;
323 flex-direction: column;
324 align-items: center;
325 }
326
327 .swal2-title {
328 position: relative;
329 max-width: 100%;
330 margin: 0 0 0.4em;
331 padding: 0;
332 color: #595959;
333 font-size: 1.875em;
334 font-weight: 600;
335 text-align: center;
336 text-transform: none;
337 word-wrap: break-word;
338 }
339
340 .swal2-actions {
341 display: flex;
342 z-index: 1;
343 flex-wrap: wrap;
344 align-items: center;
345 justify-content: center;
346 width: 100%;
347 margin: 1.25em auto 0;
348 }
349
350 .swal2-actions:not(.swal2-loading) .swal2-styled[disabled] {
351 opacity: 0.4;
352 }
353
354 .swal2-actions:not(.swal2-loading) .swal2-styled:hover {
355 background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
356 }
357
358 .swal2-actions:not(.swal2-loading) .swal2-styled:active {
359 background-image: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2));
360 }
361
362 .swal2-actions.swal2-loading .swal2-styled.swal2-confirm {
363 box-sizing: border-box;
364 width: 2.5em;
365 height: 2.5em;
366 margin: 0.46875em;
367 padding: 0;
368 -webkit-animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
369 animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
370 border: 0.25em solid transparent;
371 border-radius: 100%;
372 border-color: transparent;
373 background-color: transparent !important;
374 color: transparent !important;
375 cursor: default;
376 -webkit-user-select: none;
377 -moz-user-select: none;
378 -ms-user-select: none;
379 user-select: none;
380 }
381
382 .swal2-actions.swal2-loading .swal2-styled.swal2-cancel {
383 margin-right: 30px;
384 margin-left: 30px;
385 }
386
387 .swal2-actions.swal2-loading :not(.swal2-styled).swal2-confirm::after {
388 content: "";
389 display: inline-block;
390 width: 15px;
391 height: 15px;
392 margin-left: 5px;
393 -webkit-animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
394 animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
395 border: 3px solid #999999;
396 border-radius: 50%;
397 border-right-color: transparent;
398 box-shadow: 1px 1px 1px #fff;
399 }
400
401 .swal2-styled {
402 margin: 0.3125em;
403 padding: 0.625em 2em;
404 box-shadow: none;
405 font-weight: 500;
406 }
407
408 .swal2-styled:not([disabled]) {
409 cursor: pointer;
410 }
411
412 .swal2-styled.swal2-confirm {
413 border: 0;
414 border-radius: 0;
415 background: initial;
416 background-color: @alert4;
417 color: #fff;
418 font-size: 1.0625em;
419 }
420
421 .swal2-styled.swal2-cancel {
422 border: 0;
423 border-radius: 0;
424 background: initial;
425 background-color: #aaa;
426 color: #fff;
427 font-size: 1.0625em;
428 }
429
430 .swal2-styled:focus {
431 outline: none;
432 box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(50, 100, 150, 0.4);
433 }
434
435 .swal2-styled::-moz-focus-inner {
436 border: 0;
437 }
438
439 .swal2-footer {
440 justify-content: center;
441 margin: 1.25em 0 0;
442 padding: 1em 0 0;
443 border-top: 1px solid #eee;
444 color: #545454;
445 font-size: 1em;
446 }
447
448 .swal2-timer-progress-bar-container {
449 position: absolute;
450 right: 0;
451 bottom: 0;
452 left: 0;
453 height: 0.25em;
454 overflow: hidden;
455 border-bottom-right-radius: 0;
456 border-bottom-left-radius: 0;
457 }
458
459 .swal2-timer-progress-bar {
460 width: 100%;
461 height: 0.25em;
462 background: rgba(0, 0, 0, 0.2);
463 }
464
465 .swal2-image {
466 max-width: 100%;
467 margin: 1.25em auto;
468 }
469
470 .swal2-close {
471 position: absolute;
472 z-index: 2;
473 /* 1617 */
474 top: 0;
475 right: 0;
476 align-items: center;
477 justify-content: center;
478 width: 1.2em;
479 height: 1.2em;
480 padding: 0;
481 overflow: hidden;
482 transition: color 0.1s ease-out;
483 border: none;
484 border-radius: 0;
485 background: transparent;
486 color: #cccccc;
487 font-family: serif;
488 font-size: 2.5em;
489 line-height: 1.2;
490 cursor: pointer;
491 }
492
493 .swal2-close:hover {
494 transform: none;
495 background: transparent;
496 color: #f27474;
497 }
498
499 .swal2-close::-moz-focus-inner {
500 border: 0;
501 }
502
503 .swal2-content {
504 z-index: 1;
505 justify-content: center;
506 margin: 0;
507 padding: 0;
508 color: #545454;
509 font-size: 1.125em;
510 font-weight: normal;
511 line-height: normal;
512 text-align: center;
513 word-wrap: break-word;
514 }
515
516 .swal2-input,
517 .swal2-file,
518 .swal2-textarea,
519 .swal2-select,
520 .swal2-radio,
521 .swal2-checkbox {
522 margin: 1em auto;
523 }
524
525 .swal2-input,
526 .swal2-file,
527 .swal2-textarea {
528 box-sizing: border-box;
529 width: 100%;
530 transition: initial;
531 border: 1px solid #d9d9d9;
532 border-radius: 0;
533 background: inherit;
534 box-shadow: none;
535 color: inherit;
536 font-size: 1.125em;
537 }
538
539 .swal2-input.swal2-inputerror,
540 .swal2-file.swal2-inputerror,
541 .swal2-textarea.swal2-inputerror {
542 border-color: #f27474 !important;
543 box-shadow: 0 0 2px #f27474 !important;
544 }
545
546 .swal2-input:focus,
547 .swal2-file:focus,
548 .swal2-textarea:focus {
549 border: 1px solid #b4dbed;
550 outline: none;
551 box-shadow: 0 0 3px #c4e6f5;
552 }
553
554 .swal2-input::-webkit-input-placeholder, .swal2-file::-webkit-input-placeholder, .swal2-textarea::-webkit-input-placeholder {
555 color: #cccccc;
556 }
557
558 .swal2-input::-moz-placeholder, .swal2-file::-moz-placeholder, .swal2-textarea::-moz-placeholder {
559 color: #cccccc;
560 }
561
562 .swal2-input:-ms-input-placeholder, .swal2-file:-ms-input-placeholder, .swal2-textarea:-ms-input-placeholder {
563 color: #cccccc;
564 }
565
566 .swal2-input::-ms-input-placeholder, .swal2-file::-ms-input-placeholder, .swal2-textarea::-ms-input-placeholder {
567 color: #cccccc;
568 }
569
570 .swal2-input::placeholder,
571 .swal2-file::placeholder,
572 .swal2-textarea::placeholder {
573 color: #cccccc;
574 }
575
576 .swal2-range {
577 margin: 1em auto;
578 background: #fff;
579 }
580
581 .swal2-range input {
582 width: 80%;
583 }
584
585 .swal2-range output {
586 width: 20%;
587 color: inherit;
588 font-weight: 600;
589 text-align: center;
590 }
591
592 .swal2-range input,
593 .swal2-range output {
594 height: 2.625em;
595 padding: 0;
596 font-size: 1.125em;
597 line-height: 2.625em;
598 }
599
600 .swal2-input {
601 height: 2.625em;
602 padding: 0 0.75em;
603 }
604
605 .swal2-input[type=number] {
606 max-width: 10em;
607 }
608
609 .swal2-file {
610 background: inherit;
611 font-size: 1.125em;
612 }
613
614 .swal2-textarea {
615 height: 6.75em;
616 padding: 0.75em;
617 }
618
619 .swal2-select {
620 min-width: 50%;
621 max-width: 100%;
622 padding: 0.375em 0.625em;
623 background: inherit;
624 color: inherit;
625 font-size: 1.125em;
626 }
627
628 .swal2-radio,
629 .swal2-checkbox {
630 align-items: center;
631 justify-content: center;
632 background: #fff;
633 color: inherit;
634 }
635
636 .swal2-radio label,
637 .swal2-checkbox label {
638 margin: 0 0.6em;
639 font-size: 1.125em;
640 }
641
642 .swal2-radio input,
643 .swal2-checkbox input {
644 margin: 0 0.4em;
645 }
646
647 .swal2-validation-message {
648 display: none;
649 align-items: center;
650 justify-content: center;
651 padding: 0.625em;
652 overflow: hidden;
653 background: #f0f0f0;
654 color: #666666;
655 font-size: 1em;
656 font-weight: 300;
657 }
658
659 .swal2-validation-message::before {
660 content: "!";
661 display: inline-block;
662 width: 1.5em;
663 min-width: 1.5em;
664 height: 1.5em;
665 margin: 0 0.625em;
666 border-radius: 50%;
667 background-color: #f27474;
668 color: #fff;
669 font-weight: 600;
670 line-height: 1.5em;
671 text-align: center;
672 }
673
674 .swal2-icon {
675 position: relative;
676 box-sizing: content-box;
677 justify-content: center;
678 width: 5em;
679 height: 5em;
680 margin: 1.25em auto 1.875em;
681 border: 0.25em solid transparent;
682 border-radius: 50%;
683 font-family: inherit;
684 line-height: 5em;
685 cursor: default;
686 -webkit-user-select: none;
687 -moz-user-select: none;
688 -ms-user-select: none;
689 user-select: none;
690 }
691
692 .swal2-icon .swal2-icon-content {
693 display: flex;
694 align-items: center;
695 font-size: 3.75em;
696 }
697
698 .swal2-icon.swal2-error {
699 border-color: @alert2;
700 color: @alert2;
701 }
702
703 .swal2-icon.swal2-error .swal2-x-mark {
704 position: relative;
705 flex-grow: 1;
706 }
707
708 .swal2-icon.swal2-error [class^=swal2-x-mark-line] {
709 display: block;
710 position: absolute;
711 top: 2.3125em;
712 width: 2.9375em;
713 height: 0.3125em;
714 border-radius: 0.125em;
715 background-color: @alert2;
716 }
717
718 .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left] {
719 left: 1.0625em;
720 transform: rotate(45deg);
721 }
722
723 .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right] {
724 right: 1em;
725 transform: rotate(-45deg);
726 }
727
728 .swal2-icon.swal2-error.swal2-icon-show {
729 //-webkit-animation: swal2-animate-error-icon 0.5s;
730 // animation: swal2-animate-error-icon 0.5s;
731 }
732
733 .swal2-icon.swal2-error.swal2-icon-show .swal2-x-mark {
734 //-webkit-animation: swal2-animate-error-x-mark 0.5s;
735 // animation: swal2-animate-error-x-mark 0.5s;
736 }
737
738 .swal2-icon.swal2-warning {
739 border-color: @alert3;
740 color: @alert3;
741 }
742
743 .swal2-icon.swal2-info {
744 border-color: #9de0f6;
745 color: #3fc3ee;
746 }
747
748 .swal2-icon.swal2-question {
749 border-color: #c9dae1;
750 color: #87adbd;
751 }
752
753 .swal2-icon.swal2-success {
754 border-color: @alert1;
755 color: @alert1;
756 }
757
758 .swal2-icon.swal2-success [class^=swal2-success-circular-line] {
759 position: absolute;
760 width: 3.75em;
761 height: 7.5em;
762 transform: rotate(45deg);
763 border-radius: 50%;
764 }
765
766 .swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=left] {
767 top: -0.4375em;
768 left: -2.0635em;
769 transform: rotate(-45deg);
770 transform-origin: 3.75em 3.75em;
771 border-radius: 7.5em 0 0 7.5em;
772 }
773
774 .swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=right] {
775 top: -0.6875em;
776 left: 1.875em;
777 transform: rotate(-45deg);
778 transform-origin: 0 3.75em;
779 border-radius: 0 7.5em 7.5em 0;
780 }
781
782 .swal2-icon.swal2-success .swal2-success-ring {
783 position: absolute;
784 z-index: 2;
785 top: -0.25em;
786 left: -0.25em;
787 box-sizing: content-box;
788 width: 100%;
789 height: 100%;
790 border: 0.25em solid rgba(165, 220, 134, 0.3);
791 border-radius: 50%;
792 }
793
794 .swal2-icon.swal2-success .swal2-success-fix {
795 position: absolute;
796 z-index: 1;
797 top: 0.5em;
798 left: 1.625em;
799 width: 0.4375em;
800 height: 5.625em;
801 transform: rotate(-45deg);
802 }
803
804 .swal2-icon.swal2-success [class^=swal2-success-line] {
805 display: block;
806 position: absolute;
807 z-index: 2;
808 height: 0.3125em;
809 border-radius: 0.125em;
810 background-color: @alert1;
811 }
812
813 .swal2-icon.swal2-success [class^=swal2-success-line][class$=tip] {
814 top: 2.875em;
815 left: 0.8125em;
816 width: 1.5625em;
817 transform: rotate(45deg);
818 }
819
820 .swal2-icon.swal2-success [class^=swal2-success-line][class$=long] {
821 top: 2.375em;
822 right: 0.5em;
823 width: 2.9375em;
824 transform: rotate(-45deg);
825 }
826
827 .swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-tip {
828 -webkit-animation: swal2-animate-success-line-tip 0.75s;
829 animation: swal2-animate-success-line-tip 0.75s;
830 }
831
832 .swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-long {
833 -webkit-animation: swal2-animate-success-line-long 0.75s;
834 animation: swal2-animate-success-line-long 0.75s;
835 }
836
837 .swal2-icon.swal2-success.swal2-icon-show .swal2-success-circular-line-right {
838 -webkit-animation: swal2-rotate-success-circular-line 4.25s ease-in;
839 animation: swal2-rotate-success-circular-line 4.25s ease-in;
840 }
841
842 .swal2-progress-steps {
843 align-items: center;
844 margin: 0 0 1.25em;
845 padding: 0;
846 background: inherit;
847 font-weight: 600;
848 }
849
850 .swal2-progress-steps li {
851 display: inline-block;
852 position: relative;
853 }
854
855 .swal2-progress-steps .swal2-progress-step {
856 z-index: 20;
857 width: 2em;
858 height: 2em;
859 border-radius: 2em;
860 background: #3085d6;
861 color: #fff;
862 line-height: 2em;
863 text-align: center;
864 }
865
866 .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step {
867 background: #3085d6;
868 }
869
870 .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step ~ .swal2-progress-step {
871 background: #add8e6;
872 color: #fff;
873 }
874
875 .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step ~ .swal2-progress-step-line {
876 background: #add8e6;
877 }
878
879 .swal2-progress-steps .swal2-progress-step-line {
880 z-index: 10;
881 width: 2.5em;
882 height: 0.4em;
883 margin: 0 -1px;
884 background: #3085d6;
885 }
886
887 [class^=swal2] {
888 -webkit-tap-highlight-color: transparent;
889 }
890
891 .swal2-show {
892 -webkit-animation: swal2-show 0.3s;
893 animation: swal2-show 0.3s;
894 }
895
896 .swal2-hide {
897 -webkit-animation: swal2-hide 0.15s forwards;
898 animation: swal2-hide 0.15s forwards;
899 }
900
901 .swal2-noanimation {
902 transition: none;
903 }
904
905 .swal2-scrollbar-measure {
906 position: absolute;
907 top: -9999px;
908 width: 50px;
909 height: 50px;
910 overflow: scroll;
911 }
912
913 .swal2-rtl .swal2-close {
914 right: auto;
915 left: 0;
916 }
917
918 .swal2-rtl .swal2-timer-progress-bar {
919 right: 0;
920 left: auto;
921 }
922
923 @supports (-ms-accelerator: true) {
924 .swal2-range input {
925 width: 100% !important;
926 }
927
928 .swal2-range output {
929 display: none;
930 }
931 }
932
933 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
934 .swal2-range input {
935 width: 100% !important;
936 }
937
938 .swal2-range output {
939 display: none;
940 }
941 }
942
943 @-moz-document url-prefix() {
944 .swal2-close:focus {
945 outline: 2px solid rgba(50, 100, 150, 0.4);
946 }
947 }
948
949 @-webkit-keyframes swal2-toast-show {
950 0% {
951 transform: translateY(-0.625em) rotateZ(2deg);
952 }
953 33% {
954 transform: translateY(0) rotateZ(-2deg);
955 }
956 66% {
957 transform: translateY(0.3125em) rotateZ(2deg);
958 }
959 100% {
960 transform: translateY(0) rotateZ(0deg);
961 }
962 }
963
964 @keyframes swal2-toast-show {
965 0% {
966 transform: translateY(-0.625em) rotateZ(2deg);
967 }
968 33% {
969 transform: translateY(0) rotateZ(-2deg);
970 }
971 66% {
972 transform: translateY(0.3125em) rotateZ(2deg);
973 }
974 100% {
975 transform: translateY(0) rotateZ(0deg);
976 }
977 }
978
979 @-webkit-keyframes swal2-toast-hide {
980 100% {
981 transform: rotateZ(1deg);
982 opacity: 0;
983 }
984 }
985
986 @keyframes swal2-toast-hide {
987 100% {
988 transform: rotateZ(1deg);
989 opacity: 0;
990 }
991 }
992
993 @-webkit-keyframes swal2-toast-animate-success-line-tip {
994 0% {
995 top: 0.5625em;
996 left: 0.0625em;
997 width: 0;
998 }
999 54% {
1000 top: 0.125em;
1001 left: 0.125em;
1002 width: 0;
1003 }
1004 70% {
1005 top: 0.625em;
1006 left: -0.25em;
1007 width: 1.625em;
1008 }
1009 84% {
1010 top: 1.0625em;
1011 left: 0.75em;
1012 width: 0.5em;
1013 }
1014 100% {
1015 top: 1.125em;
1016 left: 0.1875em;
1017 width: 0.75em;
1018 }
1019 }
1020
1021 @keyframes swal2-toast-animate-success-line-tip {
1022 0% {
1023 top: 0.5625em;
1024 left: 0.0625em;
1025 width: 0;
1026 }
1027 54% {
1028 top: 0.125em;
1029 left: 0.125em;
1030 width: 0;
1031 }
1032 70% {
1033 top: 0.625em;
1034 left: -0.25em;
1035 width: 1.625em;
1036 }
1037 84% {
1038 top: 1.0625em;
1039 left: 0.75em;
1040 width: 0.5em;
1041 }
1042 100% {
1043 top: 1.125em;
1044 left: 0.1875em;
1045 width: 0.75em;
1046 }
1047 }
1048
1049 @-webkit-keyframes swal2-toast-animate-success-line-long {
1050 0% {
1051 top: 1.625em;
1052 right: 1.375em;
1053 width: 0;
1054 }
1055 65% {
1056 top: 1.25em;
1057 right: 0.9375em;
1058 width: 0;
1059 }
1060 84% {
1061 top: 0.9375em;
1062 right: 0;
1063 width: 1.125em;
1064 }
1065 100% {
1066 top: 0.9375em;
1067 right: 0.1875em;
1068 width: 1.375em;
1069 }
1070 }
1071
1072 @keyframes swal2-toast-animate-success-line-long {
1073 0% {
1074 top: 1.625em;
1075 right: 1.375em;
1076 width: 0;
1077 }
1078 65% {
1079 top: 1.25em;
1080 right: 0.9375em;
1081 width: 0;
1082 }
1083 84% {
1084 top: 0.9375em;
1085 right: 0;
1086 width: 1.125em;
1087 }
1088 100% {
1089 top: 0.9375em;
1090 right: 0.1875em;
1091 width: 1.375em;
1092 }
1093 }
1094
1095 @-webkit-keyframes swal2-show {
1096 0% {
1097 transform: scale(0.7);
1098 }
1099 45% {
1100 transform: scale(1.05);
1101 }
1102 80% {
1103 transform: scale(0.95);
1104 }
1105 100% {
1106 transform: scale(1);
1107 }
1108 }
1109
1110 @keyframes swal2-show {
1111 0% {
1112 transform: scale(0.7);
1113 }
1114 45% {
1115 transform: scale(1.05);
1116 }
1117 80% {
1118 transform: scale(0.95);
1119 }
1120 100% {
1121 transform: scale(1);
1122 }
1123 }
1124
1125 @-webkit-keyframes swal2-hide {
1126 0% {
1127 transform: scale(1);
1128 opacity: 1;
1129 }
1130 100% {
1131 transform: scale(0.5);
1132 opacity: 0;
1133 }
1134 }
1135
1136 @keyframes swal2-hide {
1137 0% {
1138 transform: scale(1);
1139 opacity: 1;
1140 }
1141 100% {
1142 transform: scale(0.5);
1143 opacity: 0;
1144 }
1145 }
1146
1147 @-webkit-keyframes swal2-animate-success-line-tip {
1148 0% {
1149 top: 1.1875em;
1150 left: 0.0625em;
1151 width: 0;
1152 }
1153 54% {
1154 top: 1.0625em;
1155 left: 0.125em;
1156 width: 0;
1157 }
1158 70% {
1159 top: 2.1875em;
1160 left: -0.375em;
1161 width: 3.125em;
1162 }
1163 84% {
1164 top: 3em;
1165 left: 1.3125em;
1166 width: 1.0625em;
1167 }
1168 100% {
1169 top: 2.8125em;
1170 left: 0.8125em;
1171 width: 1.5625em;
1172 }
1173 }
1174
1175 @keyframes swal2-animate-success-line-tip {
1176 0% {
1177 top: 1.1875em;
1178 left: 0.0625em;
1179 width: 0;
1180 }
1181 54% {
1182 top: 1.0625em;
1183 left: 0.125em;
1184 width: 0;
1185 }
1186 70% {
1187 top: 2.1875em;
1188 left: -0.375em;
1189 width: 3.125em;
1190 }
1191 84% {
1192 top: 3em;
1193 left: 1.3125em;
1194 width: 1.0625em;
1195 }
1196 100% {
1197 top: 2.8125em;
1198 left: 0.8125em;
1199 width: 1.5625em;
1200 }
1201 }
1202
1203 @-webkit-keyframes swal2-animate-success-line-long {
1204 0% {
1205 top: 3.375em;
1206 right: 2.875em;
1207 width: 0;
1208 }
1209 65% {
1210 top: 3.375em;
1211 right: 2.875em;
1212 width: 0;
1213 }
1214 84% {
1215 top: 2.1875em;
1216 right: 0;
1217 width: 3.4375em;
1218 }
1219 100% {
1220 top: 2.375em;
1221 right: 0.5em;
1222 width: 2.9375em;
1223 }
1224 }
1225
1226 @keyframes swal2-animate-success-line-long {
1227 0% {
1228 top: 3.375em;
1229 right: 2.875em;
1230 width: 0;
1231 }
1232 65% {
1233 top: 3.375em;
1234 right: 2.875em;
1235 width: 0;
1236 }
1237 84% {
1238 top: 2.1875em;
1239 right: 0;
1240 width: 3.4375em;
1241 }
1242 100% {
1243 top: 2.375em;
1244 right: 0.5em;
1245 width: 2.9375em;
1246 }
1247 }
1248
1249 @-webkit-keyframes swal2-rotate-success-circular-line {
1250 0% {
1251 transform: rotate(-45deg);
1252 }
1253 5% {
1254 transform: rotate(-45deg);
1255 }
1256 12% {
1257 transform: rotate(-405deg);
1258 }
1259 100% {
1260 transform: rotate(-405deg);
1261 }
1262 }
1263
1264 @keyframes swal2-rotate-success-circular-line {
1265 0% {
1266 transform: rotate(-45deg);
1267 }
1268 5% {
1269 transform: rotate(-45deg);
1270 }
1271 12% {
1272 transform: rotate(-405deg);
1273 }
1274 100% {
1275 transform: rotate(-405deg);
1276 }
1277 }
1278
1279 @-webkit-keyframes swal2-animate-error-x-mark {
1280 0% {
1281 margin-top: 1.625em;
1282 transform: scale(0.4);
1283 opacity: 0;
1284 }
1285 50% {
1286 margin-top: 1.625em;
1287 transform: scale(0.4);
1288 opacity: 0;
1289 }
1290 80% {
1291 margin-top: -0.375em;
1292 transform: scale(1.15);
1293 }
1294 100% {
1295 margin-top: 0;
1296 transform: scale(1);
1297 opacity: 1;
1298 }
1299 }
1300
1301 @keyframes swal2-animate-error-x-mark {
1302 0% {
1303 margin-top: 1.625em;
1304 transform: scale(0.4);
1305 opacity: 0;
1306 }
1307 50% {
1308 margin-top: 1.625em;
1309 transform: scale(0.4);
1310 opacity: 0;
1311 }
1312 80% {
1313 margin-top: -0.375em;
1314 transform: scale(1.15);
1315 }
1316 100% {
1317 margin-top: 0;
1318 transform: scale(1);
1319 opacity: 1;
1320 }
1321 }
1322
1323 @-webkit-keyframes swal2-animate-error-icon {
1324 0% {
1325 transform: rotateX(100deg);
1326 opacity: 0;
1327 }
1328 100% {
1329 transform: rotateX(0deg);
1330 opacity: 1;
1331 }
1332 }
1333
1334 @keyframes swal2-animate-error-icon {
1335 0% {
1336 transform: rotateX(100deg);
1337 opacity: 0;
1338 }
1339 100% {
1340 transform: rotateX(0deg);
1341 opacity: 1;
1342 }
1343 }
1344
1345 @-webkit-keyframes swal2-rotate-loading {
1346 0% {
1347 transform: rotate(0deg);
1348 }
1349 100% {
1350 transform: rotate(360deg);
1351 }
1352 }
1353
1354 @keyframes swal2-rotate-loading {
1355 0% {
1356 transform: rotate(0deg);
1357 }
1358 100% {
1359 transform: rotate(360deg);
1360 }
1361 }
1362
1363 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) {
1364 overflow: hidden;
1365 }
1366
1367 body.swal2-height-auto {
1368 height: auto !important;
1369 }
1370
1371 body.swal2-no-backdrop .swal2-container {
1372 top: auto;
1373 right: auto;
1374 bottom: auto;
1375 left: auto;
1376 max-width: calc(100% - 0.625em * 2);
1377 background-color: transparent !important;
1378 }
1379
1380 body.swal2-no-backdrop .swal2-container > .swal2-modal {
1381 box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
1382 }
1383
1384 body.swal2-no-backdrop .swal2-container.swal2-top {
1385 top: 0;
1386 left: 50%;
1387 transform: translateX(-50%);
1388 }
1389
1390 body.swal2-no-backdrop .swal2-container.swal2-top-start, body.swal2-no-backdrop .swal2-container.swal2-top-left {
1391 top: 0;
1392 left: 0;
1393 }
1394
1395 body.swal2-no-backdrop .swal2-container.swal2-top-end, body.swal2-no-backdrop .swal2-container.swal2-top-right {
1396 top: 0;
1397 right: 0;
1398 }
1399
1400 body.swal2-no-backdrop .swal2-container.swal2-center {
1401 top: 50%;
1402 left: 50%;
1403 transform: translate(-50%, -50%);
1404 }
1405
1406 body.swal2-no-backdrop .swal2-container.swal2-center-start, body.swal2-no-backdrop .swal2-container.swal2-center-left {
1407 top: 50%;
1408 left: 0;
1409 transform: translateY(-50%);
1410 }
1411
1412 body.swal2-no-backdrop .swal2-container.swal2-center-end, body.swal2-no-backdrop .swal2-container.swal2-center-right {
1413 top: 50%;
1414 right: 0;
1415 transform: translateY(-50%);
1416 }
1417
1418 body.swal2-no-backdrop .swal2-container.swal2-bottom {
1419 bottom: 0;
1420 left: 50%;
1421 transform: translateX(-50%);
1422 }
1423
1424 body.swal2-no-backdrop .swal2-container.swal2-bottom-start, body.swal2-no-backdrop .swal2-container.swal2-bottom-left {
1425 bottom: 0;
1426 left: 0;
1427 }
1428
1429 body.swal2-no-backdrop .swal2-container.swal2-bottom-end, body.swal2-no-backdrop .swal2-container.swal2-bottom-right {
1430 right: 0;
1431 bottom: 0;
1432 }
1433
1434 @media print {
1435 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) {
1436 overflow-y: scroll !important;
1437 }
1438
1439 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) > [aria-hidden=true] {
1440 display: none;
1441 }
1442
1443 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) .swal2-container {
1444 position: static !important;
1445 }
1446 }
1447
1448 body.swal2-toast-shown .swal2-container {
1449 background-color: transparent;
1450 }
1451
1452 body.swal2-toast-shown .swal2-container.swal2-top {
1453 top: 0;
1454 right: auto;
1455 bottom: auto;
1456 left: 50%;
1457 transform: translateX(-50%);
1458 }
1459
1460 body.swal2-toast-shown .swal2-container.swal2-top-end, body.swal2-toast-shown .swal2-container.swal2-top-right {
1461 top: 0;
1462 right: 0;
1463 bottom: auto;
1464 left: auto;
1465 }
1466
1467 body.swal2-toast-shown .swal2-container.swal2-top-start, body.swal2-toast-shown .swal2-container.swal2-top-left {
1468 top: 0;
1469 right: auto;
1470 bottom: auto;
1471 left: 0;
1472 }
1473
1474 body.swal2-toast-shown .swal2-container.swal2-center-start, body.swal2-toast-shown .swal2-container.swal2-center-left {
1475 top: 50%;
1476 right: auto;
1477 bottom: auto;
1478 left: 0;
1479 transform: translateY(-50%);
1480 }
1481
1482 body.swal2-toast-shown .swal2-container.swal2-center {
1483 top: 50%;
1484 right: auto;
1485 bottom: auto;
1486 left: 50%;
1487 transform: translate(-50%, -50%);
1488 }
1489
1490 body.swal2-toast-shown .swal2-container.swal2-center-end, body.swal2-toast-shown .swal2-container.swal2-center-right {
1491 top: 50%;
1492 right: 0;
1493 bottom: auto;
1494 left: auto;
1495 transform: translateY(-50%);
1496 }
1497
1498 body.swal2-toast-shown .swal2-container.swal2-bottom-start, body.swal2-toast-shown .swal2-container.swal2-bottom-left {
1499 top: auto;
1500 right: auto;
1501 bottom: 0;
1502 left: 0;
1503 }
1504
1505 body.swal2-toast-shown .swal2-container.swal2-bottom {
1506 top: auto;
1507 right: auto;
1508 bottom: 0;
1509 left: 50%;
1510 transform: translateX(-50%);
1511 }
1512
1513 body.swal2-toast-shown .swal2-container.swal2-bottom-end, body.swal2-toast-shown .swal2-container.swal2-bottom-right {
1514 top: auto;
1515 right: 0;
1516 bottom: 0;
1517 left: auto;
1518 }
1519
1520 body.swal2-toast-column .swal2-toast {
1521 flex-direction: column;
1522 align-items: stretch;
1523 }
1524
1525 body.swal2-toast-column .swal2-toast .swal2-actions {
1526 flex: 1;
1527 align-self: stretch;
1528 height: 2.2em;
1529 margin-top: 0.3125em;
1530 }
1531
1532 body.swal2-toast-column .swal2-toast .swal2-loading {
1533 justify-content: center;
1534 }
1535
1536 body.swal2-toast-column .swal2-toast .swal2-input {
1537 height: 2em;
1538 margin: 0.3125em auto;
1539 font-size: 1em;
1540 }
1541
1542 body.swal2-toast-column .swal2-toast .swal2-validation-message {
1543 font-size: 1em;
1544 } No newline at end of file
@@ -0,0 +1,18 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
4
5 <%def name="subject()" filter="n,trim,whitespace_filter">
6 ${email_prefix} ${exc_type_name} (${exc_id})
7 </%def>
8
9 ## plain text version of the email. Empty by default
10 <%def name="body_plaintext()" filter="n,trim">
11 NO PLAINTEXT VERSION
12 </%def>
13
14 <h4>${_('Exception `{}` generated on UTC date: {}').format(exc_traceback.get('exc_type', 'NO_TYPE'), exc_traceback.get('exc_utc_date', 'NO_DATE'))}</h4>
15 <p>
16 View exception <a href="${exc_url}">${exc_id}</a>
17 </p>
18 <pre>${exc_traceback.get('exc_message', 'NO_MESSAGE')}</pre>
@@ -0,0 +1,26 b''
1 <div tal:define="css_class css_class|field.widget.css_class;
2 style style|field.widget.style;
3 oid oid|field.oid;
4 inline getattr(field.widget, 'inline', False)"
5 tal:omit-tag="not inline">
6 ${field.start_sequence()}
7 <div tal:repeat="choice values | field.widget.values"
8 tal:omit-tag="inline"
9 class="checkbox">
10 <div tal:define="(value, title, help_block) choice">
11 <input tal:attributes="checked value in cstruct;
12 class css_class;
13 style style"
14 type="checkbox"
15 name="checkbox"
16 value="${value}"
17 id="${oid}-${repeat.choice.index}"/>
18 <label for="${oid}-${repeat.choice.index}"
19 tal:attributes="class inline and 'checkbox-inline'">
20 ${title}
21 </label>
22 <p tal:condition="help_block" class="help-block">${help_block}</p>
23 </div>
24 </div>
25 ${field.end_sequence()}
26 </div>
@@ -1,5 +1,5 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.18.3
2 current_version = 4.19.0
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:rhodecode/VERSION]
5 [bumpversion:file:rhodecode/VERSION]
@@ -5,25 +5,20 b' done = false'
5 done = true
5 done = true
6
6
7 [task:rc_tools_pinned]
7 [task:rc_tools_pinned]
8 done = true
9
8
10 [task:fixes_on_stable]
9 [task:fixes_on_stable]
11 done = true
12
10
13 [task:pip2nix_generated]
11 [task:pip2nix_generated]
14 done = true
15
12
16 [task:changelog_updated]
13 [task:changelog_updated]
17 done = true
18
14
19 [task:generate_api_docs]
15 [task:generate_api_docs]
20 done = true
16
17 [task:updated_translation]
21
18
22 [release]
19 [release]
23 state = prepared
20 state = in_progress
24 version = 4.18.3
21 version = 4.19.0
25
26 [task:updated_translation]
27
22
28 [task:generate_js_routes]
23 [task:generate_js_routes]
29
24
@@ -1,10 +1,11 b''
1
1
2 .PHONY: clean docs docs-clean docs-cleanup test test-clean test-only test-only-postgres test-only-mysql web-build generate-pkgs
2 .PHONY: clean docs docs-clean docs-cleanup test test-clean test-only test-only-postgres test-only-mysql web-build generate-pkgs pip-packages
3
3
4 NODE_PATH=./node_modules
4 NODE_PATH=./node_modules
5 WEBPACK=./node_binaries/webpack
5 WEBPACK=./node_binaries/webpack
6 GRUNT=./node_binaries/grunt
6 GRUNT=./node_binaries/grunt
7
7 # set by: PATH_TO_OUTDATED_PACKAGES=/some/path/outdated_packages.py
8 OUTDATED_PACKAGES = ${PATH_TO_OUTDATED_PACKAGES}
8
9
9 clean:
10 clean:
10 make test-clean
11 make test-clean
@@ -55,6 +56,9 b' web-build:'
55 generate-pkgs:
56 generate-pkgs:
56 nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses"
57 nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses"
57
58
59 pip-packages:
60 python ${OUTDATED_PACKAGES}
61
58 generate-js-pkgs:
62 generate-js-pkgs:
59 rm -rf node_modules && \
63 rm -rf node_modules && \
60 nix-shell pkgs/shell-generate.nix --command "node2nix --input package.json -o pkgs/node-packages.nix -e pkgs/node-env.nix -c pkgs/node-default.nix -d --flatten --nodejs-8" && \
64 nix-shell pkgs/shell-generate.nix --command "node2nix --input package.json -o pkgs/node-packages.nix -e pkgs/node-env.nix -c pkgs/node-default.nix -d --flatten --nodejs-8" && \
@@ -350,6 +350,17 b' labs_settings_active = true'
350 ; This is used to store exception from RhodeCode in shared directory
350 ; This is used to store exception from RhodeCode in shared directory
351 #exception_tracker.store_path =
351 #exception_tracker.store_path =
352
352
353 ; Send email with exception details when it happens
354 #exception_tracker.send_email = false
355
356 ; Comma separated list of recipients for exception emails,
357 ; e.g admin@rhodecode.com,devops@rhodecode.com
358 ; Can be left empty, then emails will be sent to ALL super-admins
359 #exception_tracker.send_email_recipients =
360
361 ; optional prefix to Add to email Subject
362 #exception_tracker.email_prefix = [RHODECODE ERROR]
363
353 ; File store configuration. This is used to store and serve uploaded files
364 ; File store configuration. This is used to store and serve uploaded files
354 file_store.enabled = true
365 file_store.enabled = true
355
366
@@ -605,8 +616,9 b' vcs.backends = hg, git, svn'
605 vcs.connection_timeout = 3600
616 vcs.connection_timeout = 3600
606
617
607 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
618 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
608 ; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
619 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
609 #vcs.svn.compatible_version = pre-1.8-compatible
620 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
621 #vcs.svn.compatible_version = 1.8
610
622
611
623
612 ; ####################################################
624 ; ####################################################
@@ -301,6 +301,17 b' labs_settings_active = true'
301 ; This is used to store exception from RhodeCode in shared directory
301 ; This is used to store exception from RhodeCode in shared directory
302 #exception_tracker.store_path =
302 #exception_tracker.store_path =
303
303
304 ; Send email with exception details when it happens
305 #exception_tracker.send_email = false
306
307 ; Comma separated list of recipients for exception emails,
308 ; e.g admin@rhodecode.com,devops@rhodecode.com
309 ; Can be left empty, then emails will be sent to ALL super-admins
310 #exception_tracker.send_email_recipients =
311
312 ; optional prefix to Add to email Subject
313 #exception_tracker.email_prefix = [RHODECODE ERROR]
314
304 ; File store configuration. This is used to store and serve uploaded files
315 ; File store configuration. This is used to store and serve uploaded files
305 file_store.enabled = true
316 file_store.enabled = true
306
317
@@ -556,8 +567,9 b' vcs.backends = hg, git, svn'
556 vcs.connection_timeout = 3600
567 vcs.connection_timeout = 3600
557
568
558 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
569 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
559 ; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
570 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
560 #vcs.svn.compatible_version = pre-1.8-compatible
571 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
572 #vcs.svn.compatible_version = 1.8
561
573
562
574
563 ; ####################################################
575 ; ####################################################
@@ -59,3 +59,46 b' exit the ishell after the execution::'
59
59
60 echo "%run repo_delete_task.py" | rccontrol ishell enterprise-1
60 echo "%run repo_delete_task.py" | rccontrol ishell enterprise-1
61
61
62
63
64
65 Bulk edit permissions for all repositories or groups
66 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67
68 In case when a permissions should be applied in bulk here are two ways to apply
69 the permissions onto *all* repositories and/or repository groups.
70
71 1) Start by running the interactive ishell interface
72
73 .. code-block:: bash
74 :dedent: 1
75
76 # starts the ishell interactive prompt
77 $ rccontrol ishell enterprise-1
78
79
80 2a) Add user called 'admin' into all repositories with write permission.
81 Permissions can be also `repository.read`, `repository.admin`, `repository.none`
82
83 .. code-block:: python
84 :dedent: 1
85
86 In [1]: from rhodecode.model.repo import RepoModel
87 In [2]: user = User.get_by_username('admin')
88 In [3]: permission_name = 'repository.write'
89 In [4]: for repo in Repository.get_all():
90 ...: RepoModel().grant_user_permission(repo, user, permission_name)
91 ...: Session().commit()
92
93 2b) Add user called 'admin' into all repository groups with write permission.
94 Permissions can be also can be `group.read`, `group.admin`, `group.none`
95
96 .. code-block:: python
97 :dedent: 1
98
99 In [1]: from rhodecode.model.repo import RepoModel
100 In [2]: user = User.get_by_username('admin')
101 In [3]: permission_name = 'group.write'
102 In [4]: for repo_group in RepoGroup.get_all():
103 ...: RepoGroupModel().grant_user_permission(repo_group, user, permission_name)
104 ...: Session().commit() No newline at end of file
@@ -22,8 +22,9 b' Supported Operating Systems'
22 Linux
22 Linux
23 ^^^^^
23 ^^^^^
24
24
25 * Ubuntu 14.04
25 * Ubuntu 14.04+
26 * CentOS 6.2 and 7
26 * CentOS 6.2, 7 and 8
27 * RHEL 6.2, 7 and 8
27 * Debian 7.8
28 * Debian 7.8
28 * RedHat Fedora
29 * RedHat Fedora
29 * Arch Linux
30 * Arch Linux
@@ -39,7 +39,7 b' close_pull_request'
39 comment_pull_request
39 comment_pull_request
40 --------------------
40 --------------------
41
41
42 .. py:function:: comment_pull_request(apiuser, pullrequestid, repoid=<Optional:None>, message=<Optional:None>, commit_id=<Optional:None>, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, extra_recipients=<Optional:[]>, userid=<Optional:<OptionalAttr:apiuser>>)
42 .. py:function:: comment_pull_request(apiuser, pullrequestid, repoid=<Optional:None>, message=<Optional:None>, commit_id=<Optional:None>, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, extra_recipients=<Optional:[]>, userid=<Optional:<OptionalAttr:apiuser>>, send_email=<Optional:True>)
43
43
44 Comment on the pull request specified with the `pullrequestid`,
44 Comment on the pull request specified with the `pullrequestid`,
45 in the |repo| specified by the `repoid`, and optionally change the
45 in the |repo| specified by the `repoid`, and optionally change the
@@ -70,6 +70,8 b' comment_pull_request'
70 :type extra_recipients: Optional(list)
70 :type extra_recipients: Optional(list)
71 :param userid: Comment on the pull request as this user
71 :param userid: Comment on the pull request as this user
72 :type userid: Optional(str or int)
72 :type userid: Optional(str or int)
73 :param send_email: Define if this comment should also send email notification
74 :type send_email: Optional(bool)
73
75
74 Example output:
76 Example output:
75
77
@@ -160,6 +162,7 b' get_pull_request'
160 "status" : "<status>",
162 "status" : "<status>",
161 "created_on": "<date_time_created>",
163 "created_on": "<date_time_created>",
162 "updated_on": "<date_time_updated>",
164 "updated_on": "<date_time_updated>",
165 "versions": "<number_or_versions_of_pr>",
163 "commit_ids": [
166 "commit_ids": [
164 ...
167 ...
165 "<commit_id>",
168 "<commit_id>",
@@ -249,7 +252,9 b' get_pull_request_comments'
249 },
252 },
250 "comment_text": "Example text",
253 "comment_text": "Example text",
251 "comment_type": null,
254 "comment_type": null,
252 "pull_request_version": null
255 "pull_request_version": null,
256 "comment_commit_id": None,
257 "comment_pull_request_id": <pull_request_id>
253 }
258 }
254 ],
259 ],
255 error : null
260 error : null
@@ -28,7 +28,7 b' add_field_to_repo'
28 comment_commit
28 comment_commit
29 --------------
29 --------------
30
30
31 .. py:function:: comment_commit(apiuser, repoid, commit_id, message, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, extra_recipients=<Optional:[]>, userid=<Optional:<OptionalAttr:apiuser>>)
31 .. py:function:: comment_commit(apiuser, repoid, commit_id, message, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, extra_recipients=<Optional:[]>, userid=<Optional:<OptionalAttr:apiuser>>, send_email=<Optional:True>)
32
32
33 Set a commit comment, and optionally change the status of the commit.
33 Set a commit comment, and optionally change the status of the commit.
34
34
@@ -52,6 +52,8 b' comment_commit'
52 :type extra_recipients: Optional(list)
52 :type extra_recipients: Optional(list)
53 :param userid: Set the user name of the comment creator.
53 :param userid: Set the user name of the comment creator.
54 :type userid: Optional(str or int)
54 :type userid: Optional(str or int)
55 :param send_email: Define if this comment should also send email notification
56 :type send_email: Optional(bool)
55
57
56 Example error output:
58 Example error output:
57
59
@@ -3,6 +3,21 b''
3 search methods
3 search methods
4 ==============
4 ==============
5
5
6 get_audit_logs
7 --------------
8
9 .. py:function:: get_audit_logs(apiuser, query)
10
11 return full audit logs based on the query.
12
13 Please see `example query in admin > settings > audit logs` for examples
14
15 :param apiuser: This is filled automatically from the |authtoken|.
16 :type apiuser: AuthUser
17 :param query: filter query, example: action:repo.artifact.add date:[20200401 TO 20200601]"
18 :type query: str
19
20
6 search
21 search
7 ------
22 ------
8
23
@@ -68,7 +68,7 b' Below is example setup that can be used '
68 # Multiple servers can be specified using commas
68 # Multiple servers can be specified using commas
69
69
70 *option*: `port` => `389`
70 *option*: `port` => `389`
71 # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL)
71 # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL)
72
72
73 *option*: `timeout` => `300`
73 *option*: `timeout` => `300`
74 # Timeout for LDAP connection
74 # Timeout for LDAP connection
@@ -58,7 +58,7 b' Below is example setup that can be used '
58 # Multiple servers can be specified using commas
58 # Multiple servers can be specified using commas
59
59
60 *option*: `port` => `389`
60 *option*: `port` => `389`
61 # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL)
61 # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL)
62
62
63 *option*: `timeout` => `300`
63 *option*: `timeout` => `300`
64 # Timeout for LDAP connection
64 # Timeout for LDAP connection
@@ -22,7 +22,7 b' authentication::'
22 # Multiple servers can be specified using commas
22 # Multiple servers can be specified using commas
23
23
24 *option*: `port` => `389`
24 *option*: `port` => `389`
25 # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL)
25 # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL)
26
26
27 *option*: `timeout` => `300`
27 *option*: `timeout` => `300`
28 # Timeout for LDAP connection
28 # Timeout for LDAP connection
@@ -19,7 +19,7 b' Setup Nix Package Manager'
19
19
20 To install the Nix Package Manager, please run::
20 To install the Nix Package Manager, please run::
21
21
22 $ curl https://nixos.org/releases/nix/nix-2.0.4/install | sh
22 $ curl https://releases.nixos.org/nix/nix-2.3.4/install | sh
23
23
24 or go to https://nixos.org/nix/ and follow the installation instructions.
24 or go to https://nixos.org/nix/ and follow the installation instructions.
25 Once this is correctly set up on your system, you should be able to use the
25 Once this is correctly set up on your system, you should be able to use the
@@ -47,12 +47,12 b' Switch nix to the latest STABLE channel'
47
47
48 run::
48 run::
49
49
50 nix-channel --add https://nixos.org/channels/nixos-18.03 nixpkgs
50 nix-channel --add https://nixos.org/channels/nixos-20.03 nixpkgs
51
51
52 Followed by::
52 Followed by::
53
53
54 nix-channel --update
54 nix-channel --update
55 nix-env -i nix-2.0.4
55 nix-env -i nix-2.3.4
56
56
57
57
58 Install required binaries
58 Install required binaries
@@ -3,20 +3,6 b''
3 Known Issues
3 Known Issues
4 ============
4 ============
5
5
6 Subversion Issues
7 -----------------
8
9 Limited |svn| support has been achieved for this release,
10 |release|. The following known issues are in development for improvement.
11
12 * |svn| |repo| creation:
13 Terminating the VCS Server during remote importation of |svn| |repos| leaves
14 the the process still running in the background.
15
16 * |svn| |repo| checkin/checkout:
17 |svn| cloning support is not enabled by default. Please contact support if
18 you want it enabled.
19
20 Windows Upload
6 Windows Upload
21 --------------
7 --------------
22
8
@@ -49,12 +35,16 b' shipped with the optimal configuration a'
49 To fix this issue, upgrade to |RCE| 3.3.2 or greater, and if you discover
35 To fix this issue, upgrade to |RCE| 3.3.2 or greater, and if you discover
50 memory consumption issues check the VCS Server settings.
36 memory consumption issues check the VCS Server settings.
51
37
52 Fedora 23 / Ubuntu 18.04
38 Newer Operating system locales
53 ------------------------
39 ------------------------------
54
40
55 |RCC| has a know problem with locales, due to changes in glibc 2.27+ which affects
41 |RCC| has a know problem with locales, due to changes in glibc 2.27+ which affects
56 the local-archive format, which is now incompatible with our used glibc 2.26.
42 the local-archive format, which is now incompatible with our used glibc 2.26.
57
43
44 Mostly affected are:
45 - Fedora 23+
46 - Ubuntu 18.04
47 - CentOS / RHEL 8
58
48
59 To work around this problem, you need set path to ``$LOCAL_ARCHIVE`` to the
49 To work around this problem, you need set path to ``$LOCAL_ARCHIVE`` to the
60 locale package in older pre glibc 2.27 format, or set `LC_ALL=C` in your enviroment.
50 locale package in older pre glibc 2.27 format, or set `LC_ALL=C` in your enviroment.
@@ -74,8 +64,15 b' 2. Point ``$LOCAL_ARCHIVE`` to the local'
74
64
75 $ export LOCALE_ARCHIVE=/home/USER/locale-archive # change to your path
65 $ export LOCALE_ARCHIVE=/home/USER/locale-archive # change to your path
76
66
77 This can either added in `~/.rccontrol/supervisor/supervisord.ini`
67 This should be added *both* in `enviroment` variable of `~/.rccontrol/supervisor/supervisord.ini`
78 or in user .bashrc/.zshrc etc, or via a startup script that
68 e.g
69
70 ```
71 [supervisord]
72 environment = HOME=/home/user/rhodecode,LOCALE_ARCHIVE=/YOUR-PATH/locale-archive`
73 ```
74
75 and in user .bashrc/.zshrc etc, or via a startup script that
79 runs `rccontrol self-init`
76 runs `rccontrol self-init`
80
77
81 If you happen to be running |RCC| from systemd, use the following
78 If you happen to be running |RCC| from systemd, use the following
@@ -9,6 +9,7 b' Release Notes'
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.19.0.rst
12 release-notes-4.18.3.rst
13 release-notes-4.18.3.rst
13 release-notes-4.18.2.rst
14 release-notes-4.18.2.rst
14 release-notes-4.18.1.rst
15 release-notes-4.18.1.rst
@@ -1,7 +1,7 b''
1 sphinx==1.8.2
1 sphinx==1.8.2
2 six==1.11.0
2 six==1.11.0
3 sphinx_rtd_theme==0.4.1
3 sphinx_rtd_theme==0.4.1
4 docutils==0.14.0
4 docutils==0.16.0
5 pygments==2.3.0
5 pygments==2.3.0
6 markupsafe==1.0.0
6 markupsafe==1.0.0
7 jinja2==2.9.6
7 jinja2==2.9.6
@@ -35,7 +35,8 b''
35 "<%= dirs.js.node_modules %>/moment/min/moment.min.js",
35 "<%= dirs.js.node_modules %>/moment/min/moment.min.js",
36 "<%= dirs.js.node_modules %>/clipboard/dist/clipboard.min.js",
36 "<%= dirs.js.node_modules %>/clipboard/dist/clipboard.min.js",
37 "<%= dirs.js.node_modules %>/favico.js/favico-0.3.10.min.js",
37 "<%= dirs.js.node_modules %>/favico.js/favico-0.3.10.min.js",
38 "<%= dirs.js.node_modules %>/dropzone/dist/dropzone.js",
38 "<%= dirs.js.node_modules %>/dropzone/dist/min/dropzone.min.js",
39 "<%= dirs.js.node_modules %>/sweetalert2/dist/sweetalert2.min.js",
39 "<%= dirs.js.node_modules %>/sticky-sidebar/dist/sticky-sidebar.min.js",
40 "<%= dirs.js.node_modules %>/sticky-sidebar/dist/sticky-sidebar.min.js",
40 "<%= dirs.js.node_modules %>/sticky-sidebar/dist/jquery.sticky-sidebar.min.js",
41 "<%= dirs.js.node_modules %>/sticky-sidebar/dist/jquery.sticky-sidebar.min.js",
41 "<%= dirs.js.node_modules %>/waypoints/lib/noframework.waypoints.min.js",
42 "<%= dirs.js.node_modules %>/waypoints/lib/noframework.waypoints.min.js",
@@ -23,6 +23,7 b''
23 "grunt-contrib-watch": "^0.6.1",
23 "grunt-contrib-watch": "^0.6.1",
24 "grunt-webpack": "^3.1.3",
24 "grunt-webpack": "^3.1.3",
25 "grunt-contrib-uglify": "^4.0.1",
25 "grunt-contrib-uglify": "^4.0.1",
26 "sweetalert2": "^9.10.12",
26 "jquery": "1.11.3",
27 "jquery": "1.11.3",
27 "mark.js": "8.11.1",
28 "mark.js": "8.11.1",
28 "jshint": "^2.9.1-rc3",
29 "jshint": "^2.9.1-rc3",
This diff has been collapsed as it changes many lines, (1065 lines changed) Show them Hide them
@@ -49,13 +49,13 b' let'
49 sha512 = "7+TPEAfWsRdhj1Y8UeF1759ktpVu+c3sG16rJiUC3wF9+woQ9xI1zUm2d59i7Yc3aDEJrR/Q8Y262KlOvyGVNg==";
49 sha512 = "7+TPEAfWsRdhj1Y8UeF1759ktpVu+c3sG16rJiUC3wF9+woQ9xI1zUm2d59i7Yc3aDEJrR/Q8Y262KlOvyGVNg==";
50 };
50 };
51 };
51 };
52 "@polymer/iron-autogrow-textarea-3.0.1" = {
52 "@polymer/iron-autogrow-textarea-3.0.3" = {
53 name = "_at_polymer_slash_iron-autogrow-textarea";
53 name = "_at_polymer_slash_iron-autogrow-textarea";
54 packageName = "@polymer/iron-autogrow-textarea";
54 packageName = "@polymer/iron-autogrow-textarea";
55 version = "3.0.1";
55 version = "3.0.3";
56 src = fetchurl {
56 src = fetchurl {
57 url = "https://registry.npmjs.org/@polymer/iron-autogrow-textarea/-/iron-autogrow-textarea-3.0.1.tgz";
57 url = "https://registry.npmjs.org/@polymer/iron-autogrow-textarea/-/iron-autogrow-textarea-3.0.3.tgz";
58 sha512 = "FgSL7APrOSL9Vu812sBCFlQ17hvnJsBAV2C2e1UAiaHhB+dyfLq8gGdGUpqVWuGJ50q4Y/49QwCNnLf85AdVYA==";
58 sha512 = "5r0VkWrIlm0JIp5E5wlnvkw7slK72lFRZXncmrsLZF+6n1dg2rI8jt7xpFzSmUWrqpcyXwyKaGaDvUjl3j4JLA==";
59 };
59 };
60 };
60 };
61 "@polymer/iron-behaviors-3.0.1" = {
61 "@polymer/iron-behaviors-3.0.1" = {
@@ -76,13 +76,13 b' let'
76 sha512 = "aDr0cbCNVq49q+pOqa6CZutFh+wWpwPMLpEth9swx+GkAj+gCURhuQkaUYhIo5f2egDbEioR1aeHMnPlU9dQZA==";
76 sha512 = "aDr0cbCNVq49q+pOqa6CZutFh+wWpwPMLpEth9swx+GkAj+gCURhuQkaUYhIo5f2egDbEioR1aeHMnPlU9dQZA==";
77 };
77 };
78 };
78 };
79 "@polymer/iron-fit-behavior-3.0.1" = {
79 "@polymer/iron-fit-behavior-3.0.2" = {
80 name = "_at_polymer_slash_iron-fit-behavior";
80 name = "_at_polymer_slash_iron-fit-behavior";
81 packageName = "@polymer/iron-fit-behavior";
81 packageName = "@polymer/iron-fit-behavior";
82 version = "3.0.1";
82 version = "3.0.2";
83 src = fetchurl {
83 src = fetchurl {
84 url = "https://registry.npmjs.org/@polymer/iron-fit-behavior/-/iron-fit-behavior-3.0.1.tgz";
84 url = "https://registry.npmjs.org/@polymer/iron-fit-behavior/-/iron-fit-behavior-3.0.2.tgz";
85 sha512 = "/M0B1L30k31vmwNBaGuZcxzUAhJSHoGccb/DF0CDKI/hT8UlkTvcyemaWdOpmHHLgY52ceKIkRwA3AeXrKyvaQ==";
85 sha512 = "JndryJYbBR3gSN5IlST4rCHsd01+OyvYpRO6z5Zd3C6u5V/m07TwAtcf3aXwZ8WBNt2eLG28OcvdSO7XR2v2pg==";
86 };
86 };
87 };
87 };
88 "@polymer/iron-flex-layout-3.0.1" = {
88 "@polymer/iron-flex-layout-3.0.1" = {
@@ -112,13 +112,13 b' let'
112 sha512 = "pWguPugiLYmWFV9UWxLWzZ6gm4wBwQdDy4VULKwdHCqR7OP7u98h+XDdGZsSlDPv6qoryV/e3tGHlTIT0mbzJA==";
112 sha512 = "pWguPugiLYmWFV9UWxLWzZ6gm4wBwQdDy4VULKwdHCqR7OP7u98h+XDdGZsSlDPv6qoryV/e3tGHlTIT0mbzJA==";
113 };
113 };
114 };
114 };
115 "@polymer/iron-overlay-behavior-3.0.2" = {
115 "@polymer/iron-overlay-behavior-3.0.3" = {
116 name = "_at_polymer_slash_iron-overlay-behavior";
116 name = "_at_polymer_slash_iron-overlay-behavior";
117 packageName = "@polymer/iron-overlay-behavior";
117 packageName = "@polymer/iron-overlay-behavior";
118 version = "3.0.2";
118 version = "3.0.3";
119 src = fetchurl {
119 src = fetchurl {
120 url = "https://registry.npmjs.org/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.2.tgz";
120 url = "https://registry.npmjs.org/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.3.tgz";
121 sha512 = "j1qmt6mJHCwpe1mKOvqK5kcCUPQr5LSrlqpgRDbUuLgUfNJ/vGTipjrkBlfbEUagm5FEQdc1VLPLSQP6WVuP9g==";
121 sha512 = "Q/Fp0+uOQQ145ebZ7T8Cxl4m1tUKYjyymkjcL2rXUm+aDQGb1wA1M1LYxUF5YBqd+9lipE0PTIiYwA2ZL/sznA==";
122 };
122 };
123 };
123 };
124 "@polymer/iron-resizable-behavior-3.0.1" = {
124 "@polymer/iron-resizable-behavior-3.0.1" = {
@@ -157,13 +157,13 b' let'
157 sha512 = "JRNBc+Oj9EWnmyLr7FcCr8T1KAnEHPh6mosln9BUdkM+qYaYsudSICh3cjTIbnj6AuF5OJidoLkM1dlyj0j6Zg==";
157 sha512 = "JRNBc+Oj9EWnmyLr7FcCr8T1KAnEHPh6mosln9BUdkM+qYaYsudSICh3cjTIbnj6AuF5OJidoLkM1dlyj0j6Zg==";
158 };
158 };
159 };
159 };
160 "@polymer/paper-ripple-3.0.1" = {
160 "@polymer/paper-ripple-3.0.2" = {
161 name = "_at_polymer_slash_paper-ripple";
161 name = "_at_polymer_slash_paper-ripple";
162 packageName = "@polymer/paper-ripple";
162 packageName = "@polymer/paper-ripple";
163 version = "3.0.1";
163 version = "3.0.2";
164 src = fetchurl {
164 src = fetchurl {
165 url = "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.1.tgz";
165 url = "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.2.tgz";
166 sha512 = "dgOe12GyCF1VZBLUQqnzGWlf3xb255FajNCVB1VFj/AtskYtoamnafa7m3a+1vs+C8qbg4Benn5KwgxVDSW4cg==";
166 sha512 = "DnLNvYIMsiayeICroYxx6Q6Hg1cUU8HN2sbutXazlemAlGqdq80qz3TIaVdbpbt/pvjcFGX2HtntMlPstCge8Q==";
167 };
167 };
168 };
168 };
169 "@polymer/paper-spinner-3.0.2" = {
169 "@polymer/paper-spinner-3.0.2" = {
@@ -211,13 +211,13 b' let'
211 sha512 = "yiUk09opTEnE1lK+tb501ENb+yQBi4p++Ep0eGJAHesVYKVMPNgPphVKkIizkDaU+n0SE+zXfTsRbYyOMDYXSg==";
211 sha512 = "yiUk09opTEnE1lK+tb501ENb+yQBi4p++Ep0eGJAHesVYKVMPNgPphVKkIizkDaU+n0SE+zXfTsRbYyOMDYXSg==";
212 };
212 };
213 };
213 };
214 "@polymer/polymer-3.3.0" = {
214 "@polymer/polymer-3.3.1" = {
215 name = "_at_polymer_slash_polymer";
215 name = "_at_polymer_slash_polymer";
216 packageName = "@polymer/polymer";
216 packageName = "@polymer/polymer";
217 version = "3.3.0";
217 version = "3.3.1";
218 src = fetchurl {
218 src = fetchurl {
219 url = "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.3.0.tgz";
219 url = "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.3.1.tgz";
220 sha512 = "rij7suomS7DxdBamnwr/Xa0V5hpypf7I9oYKseF2FWz5Xh2a3wJNpVjgJy1adXVCxqIyPhghsrthnfCt7EblsQ==";
220 sha512 = "8KaB48tzyMjdsHdxo5KvCAaqmTe7rYDzQAoj/pyEfq9Fp4YfUaS+/xqwYj0GbiDAUNzwkmEQ7dw9cgnRNdKO8A==";
221 };
221 };
222 };
222 };
223 "@types/clone-0.1.30" = {
223 "@types/clone-0.1.30" = {
@@ -229,13 +229,13 b' let'
229 sha1 = "e7365648c1b42136a59c7d5040637b3b5c83b614";
229 sha1 = "e7365648c1b42136a59c7d5040637b3b5c83b614";
230 };
230 };
231 };
231 };
232 "@types/node-6.14.9" = {
232 "@types/node-6.14.10" = {
233 name = "_at_types_slash_node";
233 name = "_at_types_slash_node";
234 packageName = "@types/node";
234 packageName = "@types/node";
235 version = "6.14.9";
235 version = "6.14.10";
236 src = fetchurl {
236 src = fetchurl {
237 url = "https://registry.npmjs.org/@types/node/-/node-6.14.9.tgz";
237 url = "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz";
238 sha512 = "leP/gxHunuazPdZaCvsCefPQxinqUDsCxCR5xaDUrY2MkYxQRFZZwU5e7GojyYsGB7QVtCi7iVEl/hoFXQYc+w==";
238 sha512 = "pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==";
239 };
239 };
240 };
240 };
241 "@types/parse5-2.2.34" = {
241 "@types/parse5-2.2.34" = {
@@ -409,22 +409,22 b' let'
409 sha512 = "mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==";
409 sha512 = "mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==";
410 };
410 };
411 };
411 };
412 "@webcomponents/shadycss-1.9.2" = {
412 "@webcomponents/shadycss-1.9.6" = {
413 name = "_at_webcomponents_slash_shadycss";
413 name = "_at_webcomponents_slash_shadycss";
414 packageName = "@webcomponents/shadycss";
414 packageName = "@webcomponents/shadycss";
415 version = "1.9.2";
415 version = "1.9.6";
416 src = fetchurl {
416 src = fetchurl {
417 url = "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.9.2.tgz";
417 url = "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.9.6.tgz";
418 sha512 = "GsD7RpDVrVdgC6e+D8zQia8RGNmEGQ9/qotnVPQYPrIXhGS5xSt6ZED9YmuHz3HbLqY+E54tE1EK3tjLzSCGrw==";
418 sha512 = "5fFjvP0jQJZoXK6YzYeYcIDGJ5oEsdjr1L9VaYLw5yxNd4aRz4srMpwCwldeNG0A6Hvr9igbG7fCsBeiiCXd7A==";
419 };
419 };
420 };
420 };
421 "@webcomponents/webcomponentsjs-2.3.0" = {
421 "@webcomponents/webcomponentsjs-2.4.3" = {
422 name = "_at_webcomponents_slash_webcomponentsjs";
422 name = "_at_webcomponents_slash_webcomponentsjs";
423 packageName = "@webcomponents/webcomponentsjs";
423 packageName = "@webcomponents/webcomponentsjs";
424 version = "2.3.0";
424 version = "2.4.3";
425 src = fetchurl {
425 src = fetchurl {
426 url = "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.3.0.tgz";
426 url = "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.4.3.tgz";
427 sha512 = "sR6FOrNnnncRuoJDqq9QxtRsJMbIvASw4vnJwIYKVlKO3AMc+NAr/bIQNnUiTTE9pBDTJkFpVaUdjJaRdsjmyA==";
427 sha512 = "cV4+sAmshf8ysU2USutrSRYQkJzEYKHsRCGa0CkMElGpG5747VHtkfsW3NdVIBV/m2MDKXTDydT4lkrysH7IFA==";
428 };
428 };
429 };
429 };
430 "@xtuc/ieee754-1.2.0" = {
430 "@xtuc/ieee754-1.2.0" = {
@@ -463,13 +463,13 b' let'
463 sha1 = "45e37fb39e8da3f25baee3ff5369e2bb5f22017a";
463 sha1 = "45e37fb39e8da3f25baee3ff5369e2bb5f22017a";
464 };
464 };
465 };
465 };
466 "acorn-5.7.3" = {
466 "acorn-5.7.4" = {
467 name = "acorn";
467 name = "acorn";
468 packageName = "acorn";
468 packageName = "acorn";
469 version = "5.7.3";
469 version = "5.7.4";
470 src = fetchurl {
470 src = fetchurl {
471 url = "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz";
471 url = "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz";
472 sha512 = "T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==";
472 sha512 = "1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==";
473 };
473 };
474 };
474 };
475 "acorn-dynamic-import-3.0.0" = {
475 "acorn-dynamic-import-3.0.0" = {
@@ -499,13 +499,13 b' let'
499 sha1 = "82ffb02b29e662ae53bdc20af15947706739c536";
499 sha1 = "82ffb02b29e662ae53bdc20af15947706739c536";
500 };
500 };
501 };
501 };
502 "ajv-6.10.2" = {
502 "ajv-6.12.0" = {
503 name = "ajv";
503 name = "ajv";
504 packageName = "ajv";
504 packageName = "ajv";
505 version = "6.10.2";
505 version = "6.12.0";
506 src = fetchurl {
506 src = fetchurl {
507 url = "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz";
507 url = "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz";
508 sha512 = "TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==";
508 sha512 = "D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==";
509 };
509 };
510 };
510 };
511 "ajv-keywords-3.4.1" = {
511 "ajv-keywords-3.4.1" = {
@@ -860,13 +860,13 b' let'
860 sha1 = "14342dd38dbcc94d0e5b87d763cd63612c0e794f";
860 sha1 = "14342dd38dbcc94d0e5b87d763cd63612c0e794f";
861 };
861 };
862 };
862 };
863 "aws4-1.8.0" = {
863 "aws4-1.9.1" = {
864 name = "aws4";
864 name = "aws4";
865 packageName = "aws4";
865 packageName = "aws4";
866 version = "1.8.0";
866 version = "1.9.1";
867 src = fetchurl {
867 src = fetchurl {
868 url = "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz";
868 url = "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz";
869 sha512 = "ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==";
869 sha512 = "wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==";
870 };
870 };
871 };
871 };
872 "babel-code-frame-6.26.0" = {
872 "babel-code-frame-6.26.0" = {
@@ -1445,13 +1445,22 b' let'
1445 sha512 = "Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==";
1445 sha512 = "Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==";
1446 };
1446 };
1447 };
1447 };
1448 "bluebird-3.7.1" = {
1448 "bindings-1.5.0" = {
1449 name = "bindings";
1450 packageName = "bindings";
1451 version = "1.5.0";
1452 src = fetchurl {
1453 url = "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz";
1454 sha512 = "p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==";
1455 };
1456 };
1457 "bluebird-3.7.2" = {
1449 name = "bluebird";
1458 name = "bluebird";
1450 packageName = "bluebird";
1459 packageName = "bluebird";
1451 version = "3.7.1";
1460 version = "3.7.2";
1452 src = fetchurl {
1461 src = fetchurl {
1453 url = "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz";
1462 url = "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz";
1454 sha512 = "DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==";
1463 sha512 = "XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==";
1455 };
1464 };
1456 };
1465 };
1457 "bn.js-4.11.8" = {
1466 "bn.js-4.11.8" = {
@@ -1580,13 +1589,13 b' let'
1580 sha512 = "WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==";
1589 sha512 = "WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==";
1581 };
1590 };
1582 };
1591 };
1583 "buffer-4.9.1" = {
1592 "buffer-4.9.2" = {
1584 name = "buffer";
1593 name = "buffer";
1585 packageName = "buffer";
1594 packageName = "buffer";
1586 version = "4.9.1";
1595 version = "4.9.2";
1587 src = fetchurl {
1596 src = fetchurl {
1588 url = "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz";
1597 url = "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz";
1589 sha1 = "6d1bb601b07a4efced97094132093027c95bc298";
1598 sha512 = "xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==";
1590 };
1599 };
1591 };
1600 };
1592 "buffer-from-1.1.1" = {
1601 "buffer-from-1.1.1" = {
@@ -1670,22 +1679,22 b' let'
1670 sha1 = "b534e7c734c4f81ec5fbe8aca2ad24354b962c6c";
1679 sha1 = "b534e7c734c4f81ec5fbe8aca2ad24354b962c6c";
1671 };
1680 };
1672 };
1681 };
1673 "caniuse-db-1.0.30001006" = {
1682 "caniuse-db-1.0.30001042" = {
1674 name = "caniuse-db";
1683 name = "caniuse-db";
1675 packageName = "caniuse-db";
1684 packageName = "caniuse-db";
1676 version = "1.0.30001006";
1685 version = "1.0.30001042";
1677 src = fetchurl {
1686 src = fetchurl {
1678 url = "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001006.tgz";
1687 url = "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001042.tgz";
1679 sha512 = "Xn25grc0GXATFnnEX+KP3IwEv6ZdHs4CALyLKvK8pBeeBe+hSpqy3/GyKBgEp4hn6o+bI+GNeNeQBf9PBOK0EQ==";
1688 sha512 = "2RKrB2hkLCW/8Uj32oaXj0O+N9ROo0/BF0EueWHwgs6AeeSiL+rCSsbICR3ayBJOZavgcFx65ZCw7QiafsoUFQ==";
1680 };
1689 };
1681 };
1690 };
1682 "caniuse-lite-1.0.30001006" = {
1691 "caniuse-lite-1.0.30001042" = {
1683 name = "caniuse-lite";
1692 name = "caniuse-lite";
1684 packageName = "caniuse-lite";
1693 packageName = "caniuse-lite";
1685 version = "1.0.30001006";
1694 version = "1.0.30001042";
1686 src = fetchurl {
1695 src = fetchurl {
1687 url = "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001006.tgz";
1696 url = "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz";
1688 sha512 = "MXnUVX27aGs/QINz+QG1sWSLDr3P1A3Hq5EUWoIt0T7K24DuvMxZEnh3Y5aHlJW6Bz2aApJdSewdYLd8zQnUuw==";
1697 sha512 = "igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw==";
1689 };
1698 };
1690 };
1699 };
1691 "caseless-0.12.0" = {
1700 "caseless-0.12.0" = {
@@ -1742,13 +1751,13 b' let'
1742 sha512 = "ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==";
1751 sha512 = "ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==";
1743 };
1752 };
1744 };
1753 };
1745 "chownr-1.1.3" = {
1754 "chownr-1.1.4" = {
1746 name = "chownr";
1755 name = "chownr";
1747 packageName = "chownr";
1756 packageName = "chownr";
1748 version = "1.1.3";
1757 version = "1.1.4";
1749 src = fetchurl {
1758 src = fetchurl {
1750 url = "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz";
1759 url = "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz";
1751 sha512 = "i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==";
1760 sha512 = "jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==";
1752 };
1761 };
1753 };
1762 };
1754 "chrome-trace-event-1.0.2" = {
1763 "chrome-trace-event-1.0.2" = {
@@ -1787,13 +1796,13 b' let'
1787 sha512 = "qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==";
1796 sha512 = "qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==";
1788 };
1797 };
1789 };
1798 };
1790 "clean-css-4.2.1" = {
1799 "clean-css-4.2.3" = {
1791 name = "clean-css";
1800 name = "clean-css";
1792 packageName = "clean-css";
1801 packageName = "clean-css";
1793 version = "4.2.1";
1802 version = "4.2.3";
1794 src = fetchurl {
1803 src = fetchurl {
1795 url = "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz";
1804 url = "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz";
1796 sha512 = "4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==";
1805 sha512 = "VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==";
1797 };
1806 };
1798 };
1807 };
1799 "cli-1.0.1" = {
1808 "cli-1.0.1" = {
@@ -1805,13 +1814,13 b' let'
1805 sha1 = "22817534f24bfa4950c34d532d48ecbc621b8c14";
1814 sha1 = "22817534f24bfa4950c34d532d48ecbc621b8c14";
1806 };
1815 };
1807 };
1816 };
1808 "clipboard-2.0.4" = {
1817 "clipboard-2.0.6" = {
1809 name = "clipboard";
1818 name = "clipboard";
1810 packageName = "clipboard";
1819 packageName = "clipboard";
1811 version = "2.0.4";
1820 version = "2.0.6";
1812 src = fetchurl {
1821 src = fetchurl {
1813 url = "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz";
1822 url = "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz";
1814 sha512 = "Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==";
1823 sha512 = "g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==";
1815 };
1824 };
1816 };
1825 };
1817 "cliui-2.1.0" = {
1826 "cliui-2.1.0" = {
@@ -2066,13 +2075,13 b' let'
2066 sha1 = "c20b96d8c617748aaf1c16021760cd27fcb8cb75";
2075 sha1 = "c20b96d8c617748aaf1c16021760cd27fcb8cb75";
2067 };
2076 };
2068 };
2077 };
2069 "convert-source-map-1.6.0" = {
2078 "convert-source-map-1.7.0" = {
2070 name = "convert-source-map";
2079 name = "convert-source-map";
2071 packageName = "convert-source-map";
2080 packageName = "convert-source-map";
2072 version = "1.6.0";
2081 version = "1.7.0";
2073 src = fetchurl {
2082 src = fetchurl {
2074 url = "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz";
2083 url = "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz";
2075 sha512 = "eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==";
2084 sha512 = "4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==";
2076 };
2085 };
2077 };
2086 };
2078 "copy-concurrently-1.0.5" = {
2087 "copy-concurrently-1.0.5" = {
@@ -2102,13 +2111,13 b' let'
2102 sha512 = "Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==";
2111 sha512 = "Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==";
2103 };
2112 };
2104 };
2113 };
2105 "core-js-2.6.10" = {
2114 "core-js-2.6.11" = {
2106 name = "core-js";
2115 name = "core-js";
2107 packageName = "core-js";
2116 packageName = "core-js";
2108 version = "2.6.10";
2117 version = "2.6.11";
2109 src = fetchurl {
2118 src = fetchurl {
2110 url = "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz";
2119 url = "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz";
2111 sha512 = "I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==";
2120 sha512 = "5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==";
2112 };
2121 };
2113 };
2122 };
2114 "core-util-is-1.0.2" = {
2123 "core-util-is-1.0.2" = {
@@ -2201,13 +2210,13 b' let'
2201 sha1 = "2b3a110539c5355f1cd8d314623e870b121ec858";
2210 sha1 = "2b3a110539c5355f1cd8d314623e870b121ec858";
2202 };
2211 };
2203 };
2212 };
2204 "css-selector-tokenizer-0.7.1" = {
2213 "css-selector-tokenizer-0.7.2" = {
2205 name = "css-selector-tokenizer";
2214 name = "css-selector-tokenizer";
2206 packageName = "css-selector-tokenizer";
2215 packageName = "css-selector-tokenizer";
2207 version = "0.7.1";
2216 version = "0.7.2";
2208 src = fetchurl {
2217 src = fetchurl {
2209 url = "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz";
2218 url = "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz";
2210 sha512 = "xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==";
2219 sha512 = "yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==";
2211 };
2220 };
2212 };
2221 };
2213 "css-what-2.1.3" = {
2222 "css-what-2.1.3" = {
@@ -2219,13 +2228,13 b' let'
2219 sha512 = "a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==";
2228 sha512 = "a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==";
2220 };
2229 };
2221 };
2230 };
2222 "cssesc-0.1.0" = {
2231 "cssesc-3.0.0" = {
2223 name = "cssesc";
2232 name = "cssesc";
2224 packageName = "cssesc";
2233 packageName = "cssesc";
2225 version = "0.1.0";
2234 version = "3.0.0";
2226 src = fetchurl {
2235 src = fetchurl {
2227 url = "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz";
2236 url = "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz";
2228 sha1 = "c814903e45623371a0477b40109aaafbeeaddbb4";
2237 sha512 = "/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==";
2229 };
2238 };
2230 };
2239 };
2231 "cssnano-3.10.0" = {
2240 "cssnano-3.10.0" = {
@@ -2390,13 +2399,13 b' let'
2390 sha512 = "IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==";
2399 sha512 = "IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==";
2391 };
2400 };
2392 };
2401 };
2393 "des.js-1.0.0" = {
2402 "des.js-1.0.1" = {
2394 name = "des.js";
2403 name = "des.js";
2395 packageName = "des.js";
2404 packageName = "des.js";
2396 version = "1.0.0";
2405 version = "1.0.1";
2397 src = fetchurl {
2406 src = fetchurl {
2398 url = "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz";
2407 url = "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz";
2399 sha1 = "c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc";
2408 sha512 = "Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==";
2400 };
2409 };
2401 };
2410 };
2402 "detect-file-1.0.0" = {
2411 "detect-file-1.0.0" = {
@@ -2444,13 +2453,13 b' let'
2444 sha512 = "gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==";
2453 sha512 = "gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==";
2445 };
2454 };
2446 };
2455 };
2447 "dom-serializer-0.2.1" = {
2456 "dom-serializer-0.2.2" = {
2448 name = "dom-serializer";
2457 name = "dom-serializer";
2449 packageName = "dom-serializer";
2458 packageName = "dom-serializer";
2450 version = "0.2.1";
2459 version = "0.2.2";
2451 src = fetchurl {
2460 src = fetchurl {
2452 url = "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz";
2461 url = "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz";
2453 sha512 = "sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==";
2462 sha512 = "2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==";
2454 };
2463 };
2455 };
2464 };
2456 "dom5-2.3.0" = {
2465 "dom5-2.3.0" = {
@@ -2507,13 +2516,13 b' let'
2507 sha1 = "dcd8488a26f563d61079e48c9f7b7e32373682cf";
2516 sha1 = "dcd8488a26f563d61079e48c9f7b7e32373682cf";
2508 };
2517 };
2509 };
2518 };
2510 "dropzone-5.5.1" = {
2519 "dropzone-5.7.0" = {
2511 name = "dropzone";
2520 name = "dropzone";
2512 packageName = "dropzone";
2521 packageName = "dropzone";
2513 version = "5.5.1";
2522 version = "5.7.0";
2514 src = fetchurl {
2523 src = fetchurl {
2515 url = "https://registry.npmjs.org/dropzone/-/dropzone-5.5.1.tgz";
2524 url = "https://registry.npmjs.org/dropzone/-/dropzone-5.7.0.tgz";
2516 sha512 = "3VduRWLxx9hbVr42QieQN25mx/I61/mRdUSuxAmDGdDqZIN8qtP7tcKMa3KfpJjuGjOJGYYUzzeq6eGDnkzesA==";
2525 sha512 = "kOltiZXH5cO/72I22JjE+w6BoT6uaVLfWdFMsi1PMKFkU6BZWpqRwjnsRm0o6ANGTBuZar5Piu7m/CbKqRPiYg==";
2517 };
2526 };
2518 };
2527 };
2519 "duplexer-0.1.1" = {
2528 "duplexer-0.1.1" = {
@@ -2543,22 +2552,22 b' let'
2543 sha1 = "3a83a904e54353287874c564b7549386849a98c9";
2552 sha1 = "3a83a904e54353287874c564b7549386849a98c9";
2544 };
2553 };
2545 };
2554 };
2546 "electron-to-chromium-1.3.302" = {
2555 "electron-to-chromium-1.3.412" = {
2547 name = "electron-to-chromium";
2556 name = "electron-to-chromium";
2548 packageName = "electron-to-chromium";
2557 packageName = "electron-to-chromium";
2549 version = "1.3.302";
2558 version = "1.3.412";
2550 src = fetchurl {
2559 src = fetchurl {
2551 url = "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.302.tgz";
2560 url = "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.412.tgz";
2552 sha512 = "1qConyiVEbj4xZRBXqtGR003+9tV0rJF0PS6aeO0Ln/UL637js9hdwweCl07meh/kJoI2N4W8q3R3g3F5z46ww==";
2561 sha512 = "4bVdSeJScR8fT7ERveLWbxemY5uXEHVseqMRyORosiKcTUSGtVwBkV8uLjXCqoFLeImA57Z9hbz3TOid01U4Hw==";
2553 };
2562 };
2554 };
2563 };
2555 "elliptic-6.5.1" = {
2564 "elliptic-6.5.2" = {
2556 name = "elliptic";
2565 name = "elliptic";
2557 packageName = "elliptic";
2566 packageName = "elliptic";
2558 version = "6.5.1";
2567 version = "6.5.2";
2559 src = fetchurl {
2568 src = fetchurl {
2560 url = "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz";
2569 url = "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz";
2561 sha512 = "xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==";
2570 sha512 = "f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==";
2562 };
2571 };
2563 };
2572 };
2564 "emojis-list-2.1.0" = {
2573 "emojis-list-2.1.0" = {
@@ -2570,6 +2579,15 b' let'
2570 sha1 = "4daa4d9db00f9819880c79fa457ae5b09a1fd389";
2579 sha1 = "4daa4d9db00f9819880c79fa457ae5b09a1fd389";
2571 };
2580 };
2572 };
2581 };
2582 "emojis-list-3.0.0" = {
2583 name = "emojis-list";
2584 packageName = "emojis-list";
2585 version = "3.0.0";
2586 src = fetchurl {
2587 url = "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz";
2588 sha512 = "/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==";
2589 };
2590 };
2573 "end-of-stream-1.4.4" = {
2591 "end-of-stream-1.4.4" = {
2574 name = "end-of-stream";
2592 name = "end-of-stream";
2575 packageName = "end-of-stream";
2593 packageName = "end-of-stream";
@@ -2624,22 +2642,22 b' let'
2624 sha512 = "MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==";
2642 sha512 = "MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==";
2625 };
2643 };
2626 };
2644 };
2627 "es-abstract-1.16.0" = {
2645 "es-abstract-1.17.5" = {
2628 name = "es-abstract";
2646 name = "es-abstract";
2629 packageName = "es-abstract";
2647 packageName = "es-abstract";
2630 version = "1.16.0";
2648 version = "1.17.5";
2631 src = fetchurl {
2649 src = fetchurl {
2632 url = "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz";
2650 url = "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz";
2633 sha512 = "xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==";
2651 sha512 = "BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==";
2634 };
2652 };
2635 };
2653 };
2636 "es-to-primitive-1.2.0" = {
2654 "es-to-primitive-1.2.1" = {
2637 name = "es-to-primitive";
2655 name = "es-to-primitive";
2638 packageName = "es-to-primitive";
2656 packageName = "es-to-primitive";
2639 version = "1.2.0";
2657 version = "1.2.1";
2640 src = fetchurl {
2658 src = fetchurl {
2641 url = "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz";
2659 url = "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz";
2642 sha512 = "qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==";
2660 sha512 = "QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==";
2643 };
2661 };
2644 };
2662 };
2645 "es6-templates-0.2.3" = {
2663 "es6-templates-0.2.3" = {
@@ -2741,13 +2759,13 b' let'
2741 sha1 = "8f61b75cde012b2e9eb284d4545583b5643b61ab";
2759 sha1 = "8f61b75cde012b2e9eb284d4545583b5643b61ab";
2742 };
2760 };
2743 };
2761 };
2744 "events-3.0.0" = {
2762 "events-3.1.0" = {
2745 name = "events";
2763 name = "events";
2746 packageName = "events";
2764 packageName = "events";
2747 version = "3.0.0";
2765 version = "3.1.0";
2748 src = fetchurl {
2766 src = fetchurl {
2749 url = "https://registry.npmjs.org/events/-/events-3.0.0.tgz";
2767 url = "https://registry.npmjs.org/events/-/events-3.1.0.tgz";
2750 sha512 = "Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==";
2768 sha512 = "Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==";
2751 };
2769 };
2752 };
2770 };
2753 "evp_bytestokey-1.0.3" = {
2771 "evp_bytestokey-1.0.3" = {
@@ -2849,22 +2867,22 b' let'
2849 sha1 = "96918440e3041a7a414f8c52e3c574eb3c3e1e05";
2867 sha1 = "96918440e3041a7a414f8c52e3c574eb3c3e1e05";
2850 };
2868 };
2851 };
2869 };
2852 "fast-deep-equal-2.0.1" = {
2870 "fast-deep-equal-3.1.1" = {
2853 name = "fast-deep-equal";
2871 name = "fast-deep-equal";
2854 packageName = "fast-deep-equal";
2872 packageName = "fast-deep-equal";
2855 version = "2.0.1";
2873 version = "3.1.1";
2856 src = fetchurl {
2874 src = fetchurl {
2857 url = "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz";
2875 url = "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz";
2858 sha1 = "7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49";
2876 sha512 = "8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==";
2859 };
2877 };
2860 };
2878 };
2861 "fast-json-stable-stringify-2.0.0" = {
2879 "fast-json-stable-stringify-2.1.0" = {
2862 name = "fast-json-stable-stringify";
2880 name = "fast-json-stable-stringify";
2863 packageName = "fast-json-stable-stringify";
2881 packageName = "fast-json-stable-stringify";
2864 version = "2.0.0";
2882 version = "2.1.0";
2865 src = fetchurl {
2883 src = fetchurl {
2866 url = "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz";
2884 url = "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz";
2867 sha1 = "d5142c0caee6b1189f87d3a76111064f86c8bbf2";
2885 sha512 = "lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==";
2868 };
2886 };
2869 };
2887 };
2870 "fastparse-1.1.2" = {
2888 "fastparse-1.1.2" = {
@@ -2912,6 +2930,15 b' let'
2912 sha1 = "a5e7a8ffbfa493b43b923bbd4ca89a53b63b612b";
2930 sha1 = "a5e7a8ffbfa493b43b923bbd4ca89a53b63b612b";
2913 };
2931 };
2914 };
2932 };
2933 "file-uri-to-path-1.0.0" = {
2934 name = "file-uri-to-path";
2935 packageName = "file-uri-to-path";
2936 version = "1.0.0";
2937 src = fetchurl {
2938 url = "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz";
2939 sha512 = "0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==";
2940 };
2941 };
2915 "fill-range-4.0.0" = {
2942 "fill-range-4.0.0" = {
2916 name = "fill-range";
2943 name = "fill-range";
2917 packageName = "fill-range";
2944 packageName = "fill-range";
@@ -3074,13 +3101,13 b' let'
3074 sha1 = "1504ad2523158caa40db4a2787cb01411994ea4f";
3101 sha1 = "1504ad2523158caa40db4a2787cb01411994ea4f";
3075 };
3102 };
3076 };
3103 };
3077 "fsevents-1.2.9" = {
3104 "fsevents-1.2.12" = {
3078 name = "fsevents";
3105 name = "fsevents";
3079 packageName = "fsevents";
3106 packageName = "fsevents";
3080 version = "1.2.9";
3107 version = "1.2.12";
3081 src = fetchurl {
3108 src = fetchurl {
3082 url = "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz";
3109 url = "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz";
3083 sha512 = "oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==";
3110 sha512 = "Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==";
3084 };
3111 };
3085 };
3112 };
3086 "function-bind-1.1.1" = {
3113 "function-bind-1.1.1" = {
@@ -3164,13 +3191,13 b' let'
3164 sha1 = "4a973f635b9190f715d10987d5c00fd2815ebe3d";
3191 sha1 = "4a973f635b9190f715d10987d5c00fd2815ebe3d";
3165 };
3192 };
3166 };
3193 };
3167 "glob-7.1.5" = {
3194 "glob-7.1.6" = {
3168 name = "glob";
3195 name = "glob";
3169 packageName = "glob";
3196 packageName = "glob";
3170 version = "7.1.5";
3197 version = "7.1.6";
3171 src = fetchurl {
3198 src = fetchurl {
3172 url = "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz";
3199 url = "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz";
3173 sha512 = "J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==";
3200 sha512 = "LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==";
3174 };
3201 };
3175 };
3202 };
3176 "glob-parent-3.1.0" = {
3203 "glob-parent-3.1.0" = {
@@ -3452,13 +3479,13 b' let'
3452 sha1 = "b5d454dc2199ae225699f3467e5a07f3b955bafd";
3479 sha1 = "b5d454dc2199ae225699f3467e5a07f3b955bafd";
3453 };
3480 };
3454 };
3481 };
3455 "has-symbols-1.0.0" = {
3482 "has-symbols-1.0.1" = {
3456 name = "has-symbols";
3483 name = "has-symbols";
3457 packageName = "has-symbols";
3484 packageName = "has-symbols";
3458 version = "1.0.0";
3485 version = "1.0.1";
3459 src = fetchurl {
3486 src = fetchurl {
3460 url = "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz";
3487 url = "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz";
3461 sha1 = "ba1a8f1af2a0fc39650f5c850367704122063b44";
3488 sha512 = "PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==";
3462 };
3489 };
3463 };
3490 };
3464 "has-value-0.3.1" = {
3491 "has-value-0.3.1" = {
@@ -3884,13 +3911,13 b' let'
3884 sha512 = "NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==";
3911 sha512 = "NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==";
3885 };
3912 };
3886 };
3913 };
3887 "is-callable-1.1.4" = {
3914 "is-callable-1.1.5" = {
3888 name = "is-callable";
3915 name = "is-callable";
3889 packageName = "is-callable";
3916 packageName = "is-callable";
3890 version = "1.1.4";
3917 version = "1.1.5";
3891 src = fetchurl {
3918 src = fetchurl {
3892 url = "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz";
3919 url = "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz";
3893 sha512 = "r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==";
3920 sha512 = "ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==";
3894 };
3921 };
3895 };
3922 };
3896 "is-data-descriptor-0.1.4" = {
3923 "is-data-descriptor-0.1.4" = {
@@ -3911,13 +3938,13 b' let'
3911 sha512 = "jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==";
3938 sha512 = "jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==";
3912 };
3939 };
3913 };
3940 };
3914 "is-date-object-1.0.1" = {
3941 "is-date-object-1.0.2" = {
3915 name = "is-date-object";
3942 name = "is-date-object";
3916 packageName = "is-date-object";
3943 packageName = "is-date-object";
3917 version = "1.0.1";
3944 version = "1.0.2";
3918 src = fetchurl {
3945 src = fetchurl {
3919 url = "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz";
3946 url = "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz";
3920 sha1 = "9aa20eb6aeebbff77fbd33e74ca01b33581d3a16";
3947 sha512 = "USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==";
3921 };
3948 };
3922 };
3949 };
3923 "is-descriptor-0.1.6" = {
3950 "is-descriptor-0.1.6" = {
@@ -3965,13 +3992,13 b' let'
3965 sha1 = "a88c02535791f02ed37c76a1b9ea9773c833f8c2";
3992 sha1 = "a88c02535791f02ed37c76a1b9ea9773c833f8c2";
3966 };
3993 };
3967 };
3994 };
3968 "is-finite-1.0.2" = {
3995 "is-finite-1.1.0" = {
3969 name = "is-finite";
3996 name = "is-finite";
3970 packageName = "is-finite";
3997 packageName = "is-finite";
3971 version = "1.0.2";
3998 version = "1.1.0";
3972 src = fetchurl {
3999 src = fetchurl {
3973 url = "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz";
4000 url = "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz";
3974 sha1 = "cc6677695602be550ef11e8b4aa6305342b6d0aa";
4001 sha512 = "cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==";
3975 };
4002 };
3976 };
4003 };
3977 "is-fullwidth-code-point-1.0.0" = {
4004 "is-fullwidth-code-point-1.0.0" = {
@@ -4037,13 +4064,13 b' let'
4037 sha512 = "h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==";
4064 sha512 = "h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==";
4038 };
4065 };
4039 };
4066 };
4040 "is-regex-1.0.4" = {
4067 "is-regex-1.0.5" = {
4041 name = "is-regex";
4068 name = "is-regex";
4042 packageName = "is-regex";
4069 packageName = "is-regex";
4043 version = "1.0.4";
4070 version = "1.0.5";
4044 src = fetchurl {
4071 src = fetchurl {
4045 url = "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz";
4072 url = "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz";
4046 sha1 = "5517489b547091b0930e095654ced25ee97e9491";
4073 sha512 = "vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==";
4047 };
4074 };
4048 };
4075 };
4049 "is-relative-1.0.0" = {
4076 "is-relative-1.0.0" = {
@@ -4073,13 +4100,13 b' let'
4073 sha1 = "cf61090da0d9efbcab8722deba6f032208dbb0e9";
4100 sha1 = "cf61090da0d9efbcab8722deba6f032208dbb0e9";
4074 };
4101 };
4075 };
4102 };
4076 "is-symbol-1.0.2" = {
4103 "is-symbol-1.0.3" = {
4077 name = "is-symbol";
4104 name = "is-symbol";
4078 packageName = "is-symbol";
4105 packageName = "is-symbol";
4079 version = "1.0.2";
4106 version = "1.0.3";
4080 src = fetchurl {
4107 src = fetchurl {
4081 url = "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz";
4108 url = "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz";
4082 sha512 = "HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==";
4109 sha512 = "OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==";
4083 };
4110 };
4084 };
4111 };
4085 "is-typedarray-1.0.0" = {
4112 "is-typedarray-1.0.0" = {
@@ -4172,13 +4199,13 b' let'
4172 sha1 = "dd8b74278b27102d29df63eae28308a8cfa1b583";
4199 sha1 = "dd8b74278b27102d29df63eae28308a8cfa1b583";
4173 };
4200 };
4174 };
4201 };
4175 "js-base64-2.5.1" = {
4202 "js-base64-2.5.2" = {
4176 name = "js-base64";
4203 name = "js-base64";
4177 packageName = "js-base64";
4204 packageName = "js-base64";
4178 version = "2.5.1";
4205 version = "2.5.2";
4179 src = fetchurl {
4206 src = fetchurl {
4180 url = "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz";
4207 url = "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz";
4181 sha512 = "M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==";
4208 sha512 = "Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==";
4182 };
4209 };
4183 };
4210 };
4184 "js-tokens-3.0.2" = {
4211 "js-tokens-3.0.2" = {
@@ -4235,13 +4262,13 b' let'
4235 sha1 = "46c3fec8c1892b12b0833db9bc7622176dbab34b";
4262 sha1 = "46c3fec8c1892b12b0833db9bc7622176dbab34b";
4236 };
4263 };
4237 };
4264 };
4238 "jshint-2.10.2" = {
4265 "jshint-2.11.0" = {
4239 name = "jshint";
4266 name = "jshint";
4240 packageName = "jshint";
4267 packageName = "jshint";
4241 version = "2.10.2";
4268 version = "2.11.0";
4242 src = fetchurl {
4269 src = fetchurl {
4243 url = "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz";
4270 url = "https://registry.npmjs.org/jshint/-/jshint-2.11.0.tgz";
4244 sha512 = "e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==";
4271 sha512 = "ooaD/hrBPhu35xXW4gn+o3SOuzht73gdBuffgJzrZBJZPGgGiiTvJEgTyxFvBO2nz0+X1G6etF8SzUODTlLY6Q==";
4245 };
4272 };
4246 };
4273 };
4247 "jshint-2.9.7" = {
4274 "jshint-2.9.7" = {
@@ -4361,13 +4388,13 b' let'
4361 sha512 = "NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==";
4388 sha512 = "NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==";
4362 };
4389 };
4363 };
4390 };
4364 "kind-of-6.0.2" = {
4391 "kind-of-6.0.3" = {
4365 name = "kind-of";
4392 name = "kind-of";
4366 packageName = "kind-of";
4393 packageName = "kind-of";
4367 version = "6.0.2";
4394 version = "6.0.3";
4368 src = fetchurl {
4395 src = fetchurl {
4369 url = "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz";
4396 url = "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz";
4370 sha512 = "s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==";
4397 sha512 = "dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==";
4371 };
4398 };
4372 };
4399 };
4373 "lazy-cache-1.0.4" = {
4400 "lazy-cache-1.0.4" = {
@@ -4424,13 +4451,13 b' let'
4424 sha1 = "f86e6374d43205a6e6c60e9196f17c0299bfb348";
4451 sha1 = "f86e6374d43205a6e6c60e9196f17c0299bfb348";
4425 };
4452 };
4426 };
4453 };
4427 "loader-utils-1.2.3" = {
4454 "loader-utils-1.4.0" = {
4428 name = "loader-utils";
4455 name = "loader-utils";
4429 packageName = "loader-utils";
4456 packageName = "loader-utils";
4430 version = "1.2.3";
4457 version = "1.4.0";
4431 src = fetchurl {
4458 src = fetchurl {
4432 url = "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz";
4459 url = "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz";
4433 sha512 = "fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==";
4460 sha512 = "qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==";
4434 };
4461 };
4435 };
4462 };
4436 "locate-path-2.0.0" = {
4463 "locate-path-2.0.0" = {
@@ -4622,13 +4649,13 b' let'
4622 sha1 = "180f1f9ebef8b0e638e4166ad52db879beb2ffc5";
4649 sha1 = "180f1f9ebef8b0e638e4166ad52db879beb2ffc5";
4623 };
4650 };
4624 };
4651 };
4625 "math-expression-evaluator-1.2.17" = {
4652 "math-expression-evaluator-1.2.22" = {
4626 name = "math-expression-evaluator";
4653 name = "math-expression-evaluator";
4627 packageName = "math-expression-evaluator";
4654 packageName = "math-expression-evaluator";
4628 version = "1.2.17";
4655 version = "1.2.22";
4629 src = fetchurl {
4656 src = fetchurl {
4630 url = "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz";
4657 url = "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz";
4631 sha1 = "de819fdbcd84dccd8fae59c6aeb79615b9d266ac";
4658 sha512 = "L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ==";
4632 };
4659 };
4633 };
4660 };
4634 "maxmin-2.1.0" = {
4661 "maxmin-2.1.0" = {
@@ -4703,22 +4730,22 b' let'
4703 sha512 = "x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==";
4730 sha512 = "x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==";
4704 };
4731 };
4705 };
4732 };
4706 "mime-db-1.40.0" = {
4733 "mime-db-1.43.0" = {
4707 name = "mime-db";
4734 name = "mime-db";
4708 packageName = "mime-db";
4735 packageName = "mime-db";
4709 version = "1.40.0";
4736 version = "1.43.0";
4710 src = fetchurl {
4737 src = fetchurl {
4711 url = "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz";
4738 url = "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz";
4712 sha512 = "jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==";
4739 sha512 = "+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==";
4713 };
4740 };
4714 };
4741 };
4715 "mime-types-2.1.24" = {
4742 "mime-types-2.1.26" = {
4716 name = "mime-types";
4743 name = "mime-types";
4717 packageName = "mime-types";
4744 packageName = "mime-types";
4718 version = "2.1.24";
4745 version = "2.1.26";
4719 src = fetchurl {
4746 src = fetchurl {
4720 url = "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz";
4747 url = "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz";
4721 sha512 = "WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==";
4748 sha512 = "01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==";
4722 };
4749 };
4723 };
4750 };
4724 "mimic-fn-2.1.0" = {
4751 "mimic-fn-2.1.0" = {
@@ -4775,22 +4802,13 b' let'
4775 sha512 = "yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==";
4802 sha512 = "yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==";
4776 };
4803 };
4777 };
4804 };
4778 "minimist-0.0.8" = {
4805 "minimist-1.2.5" = {
4779 name = "minimist";
4806 name = "minimist";
4780 packageName = "minimist";
4807 packageName = "minimist";
4781 version = "0.0.8";
4808 version = "1.2.5";
4782 src = fetchurl {
4809 src = fetchurl {
4783 url = "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz";
4810 url = "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz";
4784 sha1 = "857fcabfc3397d2625b8228262e86aa7a011b05d";
4811 sha512 = "FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==";
4785 };
4786 };
4787 "minimist-1.2.0" = {
4788 name = "minimist";
4789 packageName = "minimist";
4790 version = "1.2.0";
4791 src = fetchurl {
4792 url = "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz";
4793 sha1 = "a35008b20f41383eec1fb914f4cd5df79a264284";
4794 };
4812 };
4795 };
4813 };
4796 "mississippi-2.0.0" = {
4814 "mississippi-2.0.0" = {
@@ -4811,13 +4829,13 b' let'
4811 sha512 = "WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==";
4829 sha512 = "WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==";
4812 };
4830 };
4813 };
4831 };
4814 "mkdirp-0.5.1" = {
4832 "mkdirp-0.5.5" = {
4815 name = "mkdirp";
4833 name = "mkdirp";
4816 packageName = "mkdirp";
4834 packageName = "mkdirp";
4817 version = "0.5.1";
4835 version = "0.5.5";
4818 src = fetchurl {
4836 src = fetchurl {
4819 url = "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz";
4837 url = "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz";
4820 sha1 = "30057438eac6cf7f8c4767f38648d6697d75c903";
4838 sha512 = "NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==";
4821 };
4839 };
4822 };
4840 };
4823 "moment-2.24.0" = {
4841 "moment-2.24.0" = {
@@ -4829,13 +4847,13 b' let'
4829 sha512 = "bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==";
4847 sha512 = "bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==";
4830 };
4848 };
4831 };
4849 };
4832 "mousetrap-1.6.3" = {
4850 "mousetrap-1.6.5" = {
4833 name = "mousetrap";
4851 name = "mousetrap";
4834 packageName = "mousetrap";
4852 packageName = "mousetrap";
4835 version = "1.6.3";
4853 version = "1.6.5";
4836 src = fetchurl {
4854 src = fetchurl {
4837 url = "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.3.tgz";
4855 url = "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz";
4838 sha512 = "bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA==";
4856 sha512 = "QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==";
4839 };
4857 };
4840 };
4858 };
4841 "move-concurrently-1.0.1" = {
4859 "move-concurrently-1.0.1" = {
@@ -4928,13 +4946,13 b' let'
4928 sha1 = "ca7416f20a5e3f9c3b86180f96295fa3d0b52e0d";
4946 sha1 = "ca7416f20a5e3f9c3b86180f96295fa3d0b52e0d";
4929 };
4947 };
4930 };
4948 };
4931 "nopt-4.0.1" = {
4949 "nopt-4.0.3" = {
4932 name = "nopt";
4950 name = "nopt";
4933 packageName = "nopt";
4951 packageName = "nopt";
4934 version = "4.0.1";
4952 version = "4.0.3";
4935 src = fetchurl {
4953 src = fetchurl {
4936 url = "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz";
4954 url = "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz";
4937 sha1 = "d0d4685afd5415193c8c7505602d0d17cd64474d";
4955 sha512 = "CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==";
4938 };
4956 };
4939 };
4957 };
4940 "noptify-0.0.3" = {
4958 "noptify-0.0.3" = {
@@ -5045,13 +5063,13 b' let'
5045 sha1 = "7e7d858b781bd7c991a41ba975ed3812754e998c";
5063 sha1 = "7e7d858b781bd7c991a41ba975ed3812754e998c";
5046 };
5064 };
5047 };
5065 };
5048 "object-inspect-1.6.0" = {
5066 "object-inspect-1.7.0" = {
5049 name = "object-inspect";
5067 name = "object-inspect";
5050 packageName = "object-inspect";
5068 packageName = "object-inspect";
5051 version = "1.6.0";
5069 version = "1.7.0";
5052 src = fetchurl {
5070 src = fetchurl {
5053 url = "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz";
5071 url = "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz";
5054 sha512 = "GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==";
5072 sha512 = "a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==";
5055 };
5073 };
5056 };
5074 };
5057 "object-keys-1.1.1" = {
5075 "object-keys-1.1.1" = {
@@ -5072,6 +5090,15 b' let'
5072 sha1 = "f79c4493af0c5377b59fe39d395e41042dd045bb";
5090 sha1 = "f79c4493af0c5377b59fe39d395e41042dd045bb";
5073 };
5091 };
5074 };
5092 };
5093 "object.assign-4.1.0" = {
5094 name = "object.assign";
5095 packageName = "object.assign";
5096 version = "4.1.0";
5097 src = fetchurl {
5098 url = "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz";
5099 sha512 = "exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==";
5100 };
5101 };
5075 "object.defaults-1.1.0" = {
5102 "object.defaults-1.1.0" = {
5076 name = "object.defaults";
5103 name = "object.defaults";
5077 packageName = "object.defaults";
5104 packageName = "object.defaults";
@@ -5081,13 +5108,13 b' let'
5081 sha1 = "3a7f868334b407dea06da16d88d5cd29e435fecf";
5108 sha1 = "3a7f868334b407dea06da16d88d5cd29e435fecf";
5082 };
5109 };
5083 };
5110 };
5084 "object.getownpropertydescriptors-2.0.3" = {
5111 "object.getownpropertydescriptors-2.1.0" = {
5085 name = "object.getownpropertydescriptors";
5112 name = "object.getownpropertydescriptors";
5086 packageName = "object.getownpropertydescriptors";
5113 packageName = "object.getownpropertydescriptors";
5087 version = "2.0.3";
5114 version = "2.1.0";
5088 src = fetchurl {
5115 src = fetchurl {
5089 url = "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz";
5116 url = "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz";
5090 sha1 = "8758c846f5b407adab0f236e0986f14b051caa16";
5117 sha512 = "Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==";
5091 };
5118 };
5092 };
5119 };
5093 "object.map-1.0.1" = {
5120 "object.map-1.0.1" = {
@@ -5198,13 +5225,13 b' let'
5198 sha512 = "vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==";
5225 sha512 = "vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==";
5199 };
5226 };
5200 };
5227 };
5201 "p-limit-2.2.1" = {
5228 "p-limit-2.3.0" = {
5202 name = "p-limit";
5229 name = "p-limit";
5203 packageName = "p-limit";
5230 packageName = "p-limit";
5204 version = "2.2.1";
5231 version = "2.3.0";
5205 src = fetchurl {
5232 src = fetchurl {
5206 url = "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz";
5233 url = "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz";
5207 sha512 = "85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==";
5234 sha512 = "//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==";
5208 };
5235 };
5209 };
5236 };
5210 "p-locate-2.0.0" = {
5237 "p-locate-2.0.0" = {
@@ -5243,13 +5270,13 b' let'
5243 sha512 = "R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==";
5270 sha512 = "R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==";
5244 };
5271 };
5245 };
5272 };
5246 "pako-1.0.10" = {
5273 "pako-1.0.11" = {
5247 name = "pako";
5274 name = "pako";
5248 packageName = "pako";
5275 packageName = "pako";
5249 version = "1.0.10";
5276 version = "1.0.11";
5250 src = fetchurl {
5277 src = fetchurl {
5251 url = "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz";
5278 url = "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz";
5252 sha512 = "0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==";
5279 sha512 = "4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==";
5253 };
5280 };
5254 };
5281 };
5255 "parallel-transform-1.2.0" = {
5282 "parallel-transform-1.2.0" = {
@@ -6035,13 +6062,13 b' let'
6035 sha1 = "7cf4c54ef648e3813084c636dd2079e166c081d9";
6062 sha1 = "7cf4c54ef648e3813084c636dd2079e166c081d9";
6036 };
6063 };
6037 };
6064 };
6038 "readable-stream-2.3.6" = {
6065 "readable-stream-2.3.7" = {
6039 name = "readable-stream";
6066 name = "readable-stream";
6040 packageName = "readable-stream";
6067 packageName = "readable-stream";
6041 version = "2.3.6";
6068 version = "2.3.7";
6042 src = fetchurl {
6069 src = fetchurl {
6043 url = "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz";
6070 url = "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz";
6044 sha512 = "tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==";
6071 sha512 = "Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==";
6045 };
6072 };
6046 };
6073 };
6047 "readdirp-2.2.1" = {
6074 "readdirp-2.2.1" = {
@@ -6098,6 +6125,15 b' let'
6098 sha512 = "1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==";
6125 sha512 = "1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==";
6099 };
6126 };
6100 };
6127 };
6128 "regenerate-unicode-properties-8.2.0" = {
6129 name = "regenerate-unicode-properties";
6130 packageName = "regenerate-unicode-properties";
6131 version = "8.2.0";
6132 src = fetchurl {
6133 url = "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz";
6134 sha512 = "F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==";
6135 };
6136 };
6101 "regenerator-runtime-0.11.1" = {
6137 "regenerator-runtime-0.11.1" = {
6102 name = "regenerator-runtime";
6138 name = "regenerator-runtime";
6103 packageName = "regenerator-runtime";
6139 packageName = "regenerator-runtime";
@@ -6125,15 +6161,6 b' let'
6125 sha512 = "J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==";
6161 sha512 = "J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==";
6126 };
6162 };
6127 };
6163 };
6128 "regexpu-core-1.0.0" = {
6129 name = "regexpu-core";
6130 packageName = "regexpu-core";
6131 version = "1.0.0";
6132 src = fetchurl {
6133 url = "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz";
6134 sha1 = "86a763f58ee4d7c2f6b102e4764050de7ed90c6b";
6135 };
6136 };
6137 "regexpu-core-2.0.0" = {
6164 "regexpu-core-2.0.0" = {
6138 name = "regexpu-core";
6165 name = "regexpu-core";
6139 packageName = "regexpu-core";
6166 packageName = "regexpu-core";
@@ -6143,6 +6170,15 b' let'
6143 sha1 = "49d038837b8dcf8bfa5b9a42139938e6ea2ae240";
6170 sha1 = "49d038837b8dcf8bfa5b9a42139938e6ea2ae240";
6144 };
6171 };
6145 };
6172 };
6173 "regexpu-core-4.7.0" = {
6174 name = "regexpu-core";
6175 packageName = "regexpu-core";
6176 version = "4.7.0";
6177 src = fetchurl {
6178 url = "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz";
6179 sha512 = "TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==";
6180 };
6181 };
6146 "regjsgen-0.2.0" = {
6182 "regjsgen-0.2.0" = {
6147 name = "regjsgen";
6183 name = "regjsgen";
6148 packageName = "regjsgen";
6184 packageName = "regjsgen";
@@ -6152,6 +6188,15 b' let'
6152 sha1 = "6c016adeac554f75823fe37ac05b92d5a4edb1f7";
6188 sha1 = "6c016adeac554f75823fe37ac05b92d5a4edb1f7";
6153 };
6189 };
6154 };
6190 };
6191 "regjsgen-0.5.1" = {
6192 name = "regjsgen";
6193 packageName = "regjsgen";
6194 version = "0.5.1";
6195 src = fetchurl {
6196 url = "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz";
6197 sha512 = "5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==";
6198 };
6199 };
6155 "regjsparser-0.1.5" = {
6200 "regjsparser-0.1.5" = {
6156 name = "regjsparser";
6201 name = "regjsparser";
6157 packageName = "regjsparser";
6202 packageName = "regjsparser";
@@ -6161,6 +6206,15 b' let'
6161 sha1 = "7ee8f84dc6fa792d3fd0ae228d24bd949ead205c";
6206 sha1 = "7ee8f84dc6fa792d3fd0ae228d24bd949ead205c";
6162 };
6207 };
6163 };
6208 };
6209 "regjsparser-0.6.4" = {
6210 name = "regjsparser";
6211 packageName = "regjsparser";
6212 version = "0.6.4";
6213 src = fetchurl {
6214 url = "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz";
6215 sha512 = "64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==";
6216 };
6217 };
6164 "relateurl-0.2.7" = {
6218 "relateurl-0.2.7" = {
6165 name = "relateurl";
6219 name = "relateurl";
6166 packageName = "relateurl";
6220 packageName = "relateurl";
@@ -6242,13 +6296,13 b' let'
6242 sha1 = "97f717b69d48784f5f526a6c5aa8ffdda055a4d1";
6296 sha1 = "97f717b69d48784f5f526a6c5aa8ffdda055a4d1";
6243 };
6297 };
6244 };
6298 };
6245 "resolve-1.12.0" = {
6299 "resolve-1.16.0" = {
6246 name = "resolve";
6300 name = "resolve";
6247 packageName = "resolve";
6301 packageName = "resolve";
6248 version = "1.12.0";
6302 version = "1.16.0";
6249 src = fetchurl {
6303 src = fetchurl {
6250 url = "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz";
6304 url = "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz";
6251 sha512 = "B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==";
6305 sha512 = "LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==";
6252 };
6306 };
6253 };
6307 };
6254 "resolve-cwd-2.0.0" = {
6308 "resolve-cwd-2.0.0" = {
@@ -6494,13 +6548,13 b' let'
6494 sha1 = "3ff21f198cad2175f9f3b781853fd94d0d19b590";
6548 sha1 = "3ff21f198cad2175f9f3b781853fd94d0d19b590";
6495 };
6549 };
6496 };
6550 };
6497 "signal-exit-3.0.2" = {
6551 "signal-exit-3.0.3" = {
6498 name = "signal-exit";
6552 name = "signal-exit";
6499 packageName = "signal-exit";
6553 packageName = "signal-exit";
6500 version = "3.0.2";
6554 version = "3.0.3";
6501 src = fetchurl {
6555 src = fetchurl {
6502 url = "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz";
6556 url = "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz";
6503 sha1 = "b5fdc08f1287ea1178628e415e25132b73646c6d";
6557 sha512 = "VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==";
6504 };
6558 };
6505 };
6559 };
6506 "slash-1.0.0" = {
6560 "slash-1.0.0" = {
@@ -6611,13 +6665,13 b' let'
6611 sha512 = "UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==";
6665 sha512 = "UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==";
6612 };
6666 };
6613 };
6667 };
6614 "source-map-resolve-0.5.2" = {
6668 "source-map-resolve-0.5.3" = {
6615 name = "source-map-resolve";
6669 name = "source-map-resolve";
6616 packageName = "source-map-resolve";
6670 packageName = "source-map-resolve";
6617 version = "0.5.2";
6671 version = "0.5.3";
6618 src = fetchurl {
6672 src = fetchurl {
6619 url = "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz";
6673 url = "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz";
6620 sha512 = "MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==";
6674 sha512 = "Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==";
6621 };
6675 };
6622 };
6676 };
6623 "source-map-support-0.4.18" = {
6677 "source-map-support-0.4.18" = {
@@ -6719,13 +6773,13 b' let'
6719 sha512 = "+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==";
6773 sha512 = "+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==";
6720 };
6774 };
6721 };
6775 };
6722 "stream-shift-1.0.0" = {
6776 "stream-shift-1.0.1" = {
6723 name = "stream-shift";
6777 name = "stream-shift";
6724 packageName = "stream-shift";
6778 packageName = "stream-shift";
6725 version = "1.0.0";
6779 version = "1.0.1";
6726 src = fetchurl {
6780 src = fetchurl {
6727 url = "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz";
6781 url = "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz";
6728 sha1 = "d5c752825e5367e786f78e18e445ea223a155952";
6782 sha512 = "AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==";
6729 };
6783 };
6730 };
6784 };
6731 "strict-uri-encode-1.1.0" = {
6785 "strict-uri-encode-1.1.0" = {
@@ -6755,22 +6809,40 b' let'
6755 sha512 = "nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==";
6809 sha512 = "nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==";
6756 };
6810 };
6757 };
6811 };
6758 "string.prototype.trimleft-2.1.0" = {
6812 "string.prototype.trimend-1.0.1" = {
6813 name = "string.prototype.trimend";
6814 packageName = "string.prototype.trimend";
6815 version = "1.0.1";
6816 src = fetchurl {
6817 url = "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz";
6818 sha512 = "LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==";
6819 };
6820 };
6821 "string.prototype.trimleft-2.1.2" = {
6759 name = "string.prototype.trimleft";
6822 name = "string.prototype.trimleft";
6760 packageName = "string.prototype.trimleft";
6823 packageName = "string.prototype.trimleft";
6761 version = "2.1.0";
6824 version = "2.1.2";
6762 src = fetchurl {
6825 src = fetchurl {
6763 url = "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz";
6826 url = "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz";
6764 sha512 = "FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==";
6827 sha512 = "gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==";
6765 };
6828 };
6766 };
6829 };
6767 "string.prototype.trimright-2.1.0" = {
6830 "string.prototype.trimright-2.1.2" = {
6768 name = "string.prototype.trimright";
6831 name = "string.prototype.trimright";
6769 packageName = "string.prototype.trimright";
6832 packageName = "string.prototype.trimright";
6770 version = "2.1.0";
6833 version = "2.1.2";
6771 src = fetchurl {
6834 src = fetchurl {
6772 url = "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz";
6835 url = "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz";
6773 sha512 = "fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==";
6836 sha512 = "ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==";
6837 };
6838 };
6839 "string.prototype.trimstart-1.0.1" = {
6840 name = "string.prototype.trimstart";
6841 packageName = "string.prototype.trimstart";
6842 version = "1.0.1";
6843 src = fetchurl {
6844 url = "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz";
6845 sha512 = "XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==";
6774 };
6846 };
6775 };
6847 };
6776 "string_decoder-0.10.31" = {
6848 "string_decoder-0.10.31" = {
@@ -6908,6 +6980,15 b' let'
6908 sha1 = "9f5772413952135c6fefbf40afe6a4faa88b4bb5";
6980 sha1 = "9f5772413952135c6fefbf40afe6a4faa88b4bb5";
6909 };
6981 };
6910 };
6982 };
6983 "sweetalert2-9.10.12" = {
6984 name = "sweetalert2";
6985 packageName = "sweetalert2";
6986 version = "9.10.12";
6987 src = fetchurl {
6988 url = "https://registry.npmjs.org/sweetalert2/-/sweetalert2-9.10.12.tgz";
6989 sha512 = "RnarmbDGTPmwecJbaVdq5LvlzbVReIOtPk0huPnXOE19G00xMxGcTY0wjt9AjwsexUnLivLXc3b6nD6+D6NlGg==";
6990 };
6991 };
6911 "tapable-0.2.9" = {
6992 "tapable-0.2.9" = {
6912 name = "tapable";
6993 name = "tapable";
6913 packageName = "tapable";
6994 packageName = "tapable";
@@ -7052,13 +7133,13 b' let'
7052 sha1 = "30c6203e1e66b841a88701ed8858f1725d94b026";
7133 sha1 = "30c6203e1e66b841a88701ed8858f1725d94b026";
7053 };
7134 };
7054 };
7135 };
7055 "tslib-1.10.0" = {
7136 "tslib-1.11.1" = {
7056 name = "tslib";
7137 name = "tslib";
7057 packageName = "tslib";
7138 packageName = "tslib";
7058 version = "1.10.0";
7139 version = "1.11.1";
7059 src = fetchurl {
7140 src = fetchurl {
7060 url = "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz";
7141 url = "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz";
7061 sha512 = "qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==";
7142 sha512 = "aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==";
7062 };
7143 };
7063 };
7144 };
7064 "tty-browserify-0.0.0" = {
7145 "tty-browserify-0.0.0" = {
@@ -7124,13 +7205,13 b' let'
7124 sha512 = "Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==";
7205 sha512 = "Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==";
7125 };
7206 };
7126 };
7207 };
7127 "uglify-js-3.6.7" = {
7208 "uglify-js-3.9.1" = {
7128 name = "uglify-js";
7209 name = "uglify-js";
7129 packageName = "uglify-js";
7210 packageName = "uglify-js";
7130 version = "3.6.7";
7211 version = "3.9.1";
7131 src = fetchurl {
7212 src = fetchurl {
7132 url = "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.7.tgz";
7213 url = "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz";
7133 sha512 = "4sXQDzmdnoXiO+xvmTzQsfIiwrjUCSA95rSP4SEd8tDb51W2TiDOlL76Hl+Kw0Ie42PSItCW8/t6pBNCF2R48A==";
7214 sha512 = "JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA==";
7134 };
7215 };
7135 };
7216 };
7136 "uglify-to-browserify-1.0.2" = {
7217 "uglify-to-browserify-1.0.2" = {
@@ -7196,6 +7277,42 b' let'
7196 sha1 = "8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b";
7277 sha1 = "8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b";
7197 };
7278 };
7198 };
7279 };
7280 "unicode-canonical-property-names-ecmascript-1.0.4" = {
7281 name = "unicode-canonical-property-names-ecmascript";
7282 packageName = "unicode-canonical-property-names-ecmascript";
7283 version = "1.0.4";
7284 src = fetchurl {
7285 url = "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz";
7286 sha512 = "jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==";
7287 };
7288 };
7289 "unicode-match-property-ecmascript-1.0.4" = {
7290 name = "unicode-match-property-ecmascript";
7291 packageName = "unicode-match-property-ecmascript";
7292 version = "1.0.4";
7293 src = fetchurl {
7294 url = "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz";
7295 sha512 = "L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==";
7296 };
7297 };
7298 "unicode-match-property-value-ecmascript-1.2.0" = {
7299 name = "unicode-match-property-value-ecmascript";
7300 packageName = "unicode-match-property-value-ecmascript";
7301 version = "1.2.0";
7302 src = fetchurl {
7303 url = "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz";
7304 sha512 = "wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==";
7305 };
7306 };
7307 "unicode-property-aliases-ecmascript-1.1.0" = {
7308 name = "unicode-property-aliases-ecmascript";
7309 packageName = "unicode-property-aliases-ecmascript";
7310 version = "1.1.0";
7311 src = fetchurl {
7312 url = "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz";
7313 sha512 = "PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==";
7314 };
7315 };
7199 "union-value-1.0.1" = {
7316 "union-value-1.0.1" = {
7200 name = "union-value";
7317 name = "union-value";
7201 packageName = "union-value";
7318 packageName = "union-value";
@@ -7358,13 +7475,13 b' let'
7358 sha1 = "8a16a05d445657a3aea5eecc5b12a4fa5379772c";
7475 sha1 = "8a16a05d445657a3aea5eecc5b12a4fa5379772c";
7359 };
7476 };
7360 };
7477 };
7361 "uuid-3.3.3" = {
7478 "uuid-3.4.0" = {
7362 name = "uuid";
7479 name = "uuid";
7363 packageName = "uuid";
7480 packageName = "uuid";
7364 version = "3.3.3";
7481 version = "3.4.0";
7365 src = fetchurl {
7482 src = fetchurl {
7366 url = "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz";
7483 url = "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz";
7367 sha512 = "pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==";
7484 sha512 = "HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==";
7368 };
7485 };
7369 };
7486 };
7370 "v8-compile-cache-2.1.0" = {
7487 "v8-compile-cache-2.1.0" = {
@@ -7385,13 +7502,13 b' let'
7385 sha512 = "amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==";
7502 sha512 = "amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==";
7386 };
7503 };
7387 };
7504 };
7388 "vendors-1.0.3" = {
7505 "vendors-1.0.4" = {
7389 name = "vendors";
7506 name = "vendors";
7390 packageName = "vendors";
7507 packageName = "vendors";
7391 version = "1.0.3";
7508 version = "1.0.4";
7392 src = fetchurl {
7509 src = fetchurl {
7393 url = "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz";
7510 url = "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz";
7394 sha512 = "fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==";
7511 sha512 = "/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==";
7395 };
7512 };
7396 };
7513 };
7397 "verror-1.10.0" = {
7514 "verror-1.10.0" = {
@@ -7403,22 +7520,22 b' let'
7403 sha1 = "3a105ca17053af55d6e270c1f8288682e18da400";
7520 sha1 = "3a105ca17053af55d6e270c1f8288682e18da400";
7404 };
7521 };
7405 };
7522 };
7406 "vm-browserify-1.1.0" = {
7523 "vm-browserify-1.1.2" = {
7407 name = "vm-browserify";
7524 name = "vm-browserify";
7408 packageName = "vm-browserify";
7525 packageName = "vm-browserify";
7409 version = "1.1.0";
7526 version = "1.1.2";
7410 src = fetchurl {
7527 src = fetchurl {
7411 url = "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz";
7528 url = "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz";
7412 sha512 = "iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==";
7529 sha512 = "2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==";
7413 };
7530 };
7414 };
7531 };
7415 "watchpack-1.6.0" = {
7532 "watchpack-1.6.1" = {
7416 name = "watchpack";
7533 name = "watchpack";
7417 packageName = "watchpack";
7534 packageName = "watchpack";
7418 version = "1.6.0";
7535 version = "1.6.1";
7419 src = fetchurl {
7536 src = fetchurl {
7420 url = "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz";
7537 url = "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz";
7421 sha512 = "i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==";
7538 sha512 = "+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==";
7422 };
7539 };
7423 };
7540 };
7424 "waypoints-4.0.1" = {
7541 "waypoints-4.0.1" = {
@@ -7622,27 +7739,27 b' let'
7622 sources."@polymer/iron-a11y-keys-3.0.1"
7739 sources."@polymer/iron-a11y-keys-3.0.1"
7623 sources."@polymer/iron-a11y-keys-behavior-3.0.1"
7740 sources."@polymer/iron-a11y-keys-behavior-3.0.1"
7624 sources."@polymer/iron-ajax-3.0.1"
7741 sources."@polymer/iron-ajax-3.0.1"
7625 sources."@polymer/iron-autogrow-textarea-3.0.1"
7742 sources."@polymer/iron-autogrow-textarea-3.0.3"
7626 sources."@polymer/iron-behaviors-3.0.1"
7743 sources."@polymer/iron-behaviors-3.0.1"
7627 sources."@polymer/iron-checked-element-behavior-3.0.1"
7744 sources."@polymer/iron-checked-element-behavior-3.0.1"
7628 sources."@polymer/iron-fit-behavior-3.0.1"
7745 sources."@polymer/iron-fit-behavior-3.0.2"
7629 sources."@polymer/iron-flex-layout-3.0.1"
7746 sources."@polymer/iron-flex-layout-3.0.1"
7630 sources."@polymer/iron-form-element-behavior-3.0.1"
7747 sources."@polymer/iron-form-element-behavior-3.0.1"
7631 sources."@polymer/iron-meta-3.0.1"
7748 sources."@polymer/iron-meta-3.0.1"
7632 sources."@polymer/iron-overlay-behavior-3.0.2"
7749 sources."@polymer/iron-overlay-behavior-3.0.3"
7633 sources."@polymer/iron-resizable-behavior-3.0.1"
7750 sources."@polymer/iron-resizable-behavior-3.0.1"
7634 sources."@polymer/iron-validatable-behavior-3.0.1"
7751 sources."@polymer/iron-validatable-behavior-3.0.1"
7635 sources."@polymer/paper-behaviors-3.0.1"
7752 sources."@polymer/paper-behaviors-3.0.1"
7636 sources."@polymer/paper-button-3.0.1"
7753 sources."@polymer/paper-button-3.0.1"
7637 sources."@polymer/paper-ripple-3.0.1"
7754 sources."@polymer/paper-ripple-3.0.2"
7638 sources."@polymer/paper-spinner-3.0.2"
7755 sources."@polymer/paper-spinner-3.0.2"
7639 sources."@polymer/paper-styles-3.0.1"
7756 sources."@polymer/paper-styles-3.0.1"
7640 sources."@polymer/paper-toast-3.0.1"
7757 sources."@polymer/paper-toast-3.0.1"
7641 sources."@polymer/paper-toggle-button-3.0.1"
7758 sources."@polymer/paper-toggle-button-3.0.1"
7642 sources."@polymer/paper-tooltip-3.0.1"
7759 sources."@polymer/paper-tooltip-3.0.1"
7643 sources."@polymer/polymer-3.3.0"
7760 sources."@polymer/polymer-3.3.1"
7644 sources."@types/clone-0.1.30"
7761 sources."@types/clone-0.1.30"
7645 sources."@types/node-6.14.9"
7762 sources."@types/node-6.14.10"
7646 sources."@types/parse5-2.2.34"
7763 sources."@types/parse5-2.2.34"
7647 sources."@webassemblyjs/ast-1.7.10"
7764 sources."@webassemblyjs/ast-1.7.10"
7648 sources."@webassemblyjs/floating-point-hex-parser-1.7.10"
7765 sources."@webassemblyjs/floating-point-hex-parser-1.7.10"
@@ -7662,12 +7779,12 b' let'
7662 sources."@webassemblyjs/wasm-parser-1.7.10"
7779 sources."@webassemblyjs/wasm-parser-1.7.10"
7663 sources."@webassemblyjs/wast-parser-1.7.10"
7780 sources."@webassemblyjs/wast-parser-1.7.10"
7664 sources."@webassemblyjs/wast-printer-1.7.10"
7781 sources."@webassemblyjs/wast-printer-1.7.10"
7665 sources."@webcomponents/shadycss-1.9.2"
7782 sources."@webcomponents/shadycss-1.9.6"
7666 sources."@webcomponents/webcomponentsjs-2.3.0"
7783 sources."@webcomponents/webcomponentsjs-2.4.3"
7667 sources."@xtuc/ieee754-1.2.0"
7784 sources."@xtuc/ieee754-1.2.0"
7668 sources."@xtuc/long-4.2.1"
7785 sources."@xtuc/long-4.2.1"
7669 sources."abbrev-1.1.1"
7786 sources."abbrev-1.1.1"
7670 sources."acorn-5.7.3"
7787 sources."acorn-5.7.4"
7671 sources."acorn-dynamic-import-3.0.0"
7788 sources."acorn-dynamic-import-3.0.0"
7672 (sources."acorn-jsx-3.0.1" // {
7789 (sources."acorn-jsx-3.0.1" // {
7673 dependencies = [
7790 dependencies = [
@@ -7727,7 +7844,7 b' let'
7727 ];
7844 ];
7728 })
7845 })
7729 sources."aws-sign2-0.6.0"
7846 sources."aws-sign2-0.6.0"
7730 sources."aws4-1.8.0"
7847 sources."aws4-1.9.1"
7731 (sources."babel-code-frame-6.26.0" // {
7848 (sources."babel-code-frame-6.26.0" // {
7732 dependencies = [
7849 dependencies = [
7733 sources."ansi-regex-2.1.1"
7850 sources."ansi-regex-2.1.1"
@@ -7841,7 +7958,8 b' let'
7841 sources."bcrypt-pbkdf-1.0.2"
7958 sources."bcrypt-pbkdf-1.0.2"
7842 sources."big.js-5.2.2"
7959 sources."big.js-5.2.2"
7843 sources."binary-extensions-1.13.1"
7960 sources."binary-extensions-1.13.1"
7844 sources."bluebird-3.7.1"
7961 sources."bindings-1.5.0"
7962 sources."bluebird-3.7.2"
7845 sources."bn.js-4.11.8"
7963 sources."bn.js-4.11.8"
7846 sources."boolbase-1.0.0"
7964 sources."boolbase-1.0.0"
7847 sources."boom-2.10.1"
7965 sources."boom-2.10.1"
@@ -7859,13 +7977,13 b' let'
7859 sources."browserify-sign-4.0.4"
7977 sources."browserify-sign-4.0.4"
7860 sources."browserify-zlib-0.2.0"
7978 sources."browserify-zlib-0.2.0"
7861 sources."browserslist-3.2.8"
7979 sources."browserslist-3.2.8"
7862 sources."buffer-4.9.1"
7980 sources."buffer-4.9.2"
7863 sources."buffer-from-1.1.1"
7981 sources."buffer-from-1.1.1"
7864 sources."buffer-xor-1.0.3"
7982 sources."buffer-xor-1.0.3"
7865 sources."builtin-status-codes-3.0.0"
7983 sources."builtin-status-codes-3.0.0"
7866 (sources."cacache-10.0.4" // {
7984 (sources."cacache-10.0.4" // {
7867 dependencies = [
7985 dependencies = [
7868 sources."glob-7.1.5"
7986 sources."glob-7.1.6"
7869 sources."graceful-fs-4.2.3"
7987 sources."graceful-fs-4.2.3"
7870 sources."lru-cache-4.1.5"
7988 sources."lru-cache-4.1.5"
7871 sources."minimatch-3.0.4"
7989 sources."minimatch-3.0.4"
@@ -7880,8 +7998,8 b' let'
7880 sources."browserslist-1.7.7"
7998 sources."browserslist-1.7.7"
7881 ];
7999 ];
7882 })
8000 })
7883 sources."caniuse-db-1.0.30001006"
8001 sources."caniuse-db-1.0.30001042"
7884 sources."caniuse-lite-1.0.30001006"
8002 sources."caniuse-lite-1.0.30001042"
7885 sources."caseless-0.12.0"
8003 sources."caseless-0.12.0"
7886 sources."center-align-0.1.3"
8004 sources."center-align-0.1.3"
7887 sources."chalk-0.5.1"
8005 sources."chalk-0.5.1"
@@ -7890,7 +8008,7 b' let'
7890 sources."is-glob-4.0.1"
8008 sources."is-glob-4.0.1"
7891 ];
8009 ];
7892 })
8010 })
7893 sources."chownr-1.1.3"
8011 sources."chownr-1.1.4"
7894 sources."chrome-trace-event-1.0.2"
8012 sources."chrome-trace-event-1.0.2"
7895 sources."cipher-base-1.0.4"
8013 sources."cipher-base-1.0.4"
7896 (sources."clap-1.2.3" // {
8014 (sources."clap-1.2.3" // {
@@ -7920,18 +8038,18 b' let'
7920 sources."kind-of-5.1.0"
8038 sources."kind-of-5.1.0"
7921 ];
8039 ];
7922 })
8040 })
7923 (sources."clean-css-4.2.1" // {
8041 (sources."clean-css-4.2.3" // {
7924 dependencies = [
8042 dependencies = [
7925 sources."source-map-0.6.1"
8043 sources."source-map-0.6.1"
7926 ];
8044 ];
7927 })
8045 })
7928 (sources."cli-1.0.1" // {
8046 (sources."cli-1.0.1" // {
7929 dependencies = [
8047 dependencies = [
7930 sources."glob-7.1.5"
8048 sources."glob-7.1.6"
7931 sources."minimatch-3.0.4"
8049 sources."minimatch-3.0.4"
7932 ];
8050 ];
7933 })
8051 })
7934 sources."clipboard-2.0.4"
8052 sources."clipboard-2.0.6"
7935 (sources."cliui-4.1.0" // {
8053 (sources."cliui-4.1.0" // {
7936 dependencies = [
8054 dependencies = [
7937 sources."ansi-regex-3.0.0"
8055 sources."ansi-regex-3.0.0"
@@ -7957,21 +8075,21 b' let'
7957 sources."concat-map-0.0.1"
8075 sources."concat-map-0.0.1"
7958 (sources."concat-stream-1.6.2" // {
8076 (sources."concat-stream-1.6.2" // {
7959 dependencies = [
8077 dependencies = [
7960 sources."readable-stream-2.3.6"
8078 sources."readable-stream-2.3.7"
7961 sources."safe-buffer-5.1.2"
8079 sources."safe-buffer-5.1.2"
7962 sources."string_decoder-1.1.1"
8080 sources."string_decoder-1.1.1"
7963 ];
8081 ];
7964 })
8082 })
7965 sources."console-browserify-1.1.0"
8083 sources."console-browserify-1.1.0"
7966 sources."constants-browserify-1.0.0"
8084 sources."constants-browserify-1.0.0"
7967 (sources."convert-source-map-1.6.0" // {
8085 (sources."convert-source-map-1.7.0" // {
7968 dependencies = [
8086 dependencies = [
7969 sources."safe-buffer-5.1.2"
8087 sources."safe-buffer-5.1.2"
7970 ];
8088 ];
7971 })
8089 })
7972 (sources."copy-concurrently-1.0.5" // {
8090 (sources."copy-concurrently-1.0.5" // {
7973 dependencies = [
8091 dependencies = [
7974 sources."glob-7.1.5"
8092 sources."glob-7.1.6"
7975 sources."minimatch-3.0.4"
8093 sources."minimatch-3.0.4"
7976 sources."rimraf-2.7.1"
8094 sources."rimraf-2.7.1"
7977 ];
8095 ];
@@ -7983,7 +8101,7 b' let'
7983 sources."minimatch-3.0.4"
8101 sources."minimatch-3.0.4"
7984 ];
8102 ];
7985 })
8103 })
7986 sources."core-js-2.6.10"
8104 sources."core-js-2.6.11"
7987 sources."core-util-is-1.0.2"
8105 sources."core-util-is-1.0.2"
7988 sources."create-ecdh-4.0.3"
8106 sources."create-ecdh-4.0.3"
7989 sources."create-hash-1.2.0"
8107 sources."create-hash-1.2.0"
@@ -7998,13 +8116,16 b' let'
7998 sources."css-color-names-0.0.4"
8116 sources."css-color-names-0.0.4"
7999 sources."css-loader-0.28.11"
8117 sources."css-loader-0.28.11"
8000 sources."css-select-1.2.0"
8118 sources."css-select-1.2.0"
8001 (sources."css-selector-tokenizer-0.7.1" // {
8119 (sources."css-selector-tokenizer-0.7.2" // {
8002 dependencies = [
8120 dependencies = [
8003 sources."regexpu-core-1.0.0"
8121 sources."jsesc-0.5.0"
8122 sources."regexpu-core-4.7.0"
8123 sources."regjsgen-0.5.1"
8124 sources."regjsparser-0.6.4"
8004 ];
8125 ];
8005 })
8126 })
8006 sources."css-what-2.1.3"
8127 sources."css-what-2.1.3"
8007 sources."cssesc-0.1.0"
8128 sources."cssesc-3.0.0"
8008 sources."cssnano-3.10.0"
8129 sources."cssnano-3.10.0"
8009 sources."csso-2.3.2"
8130 sources."csso-2.3.2"
8010 sources."cyclist-1.0.1"
8131 sources."cyclist-1.0.1"
@@ -8024,13 +8145,13 b' let'
8024 sources."defined-1.0.0"
8145 sources."defined-1.0.0"
8025 sources."delayed-stream-1.0.0"
8146 sources."delayed-stream-1.0.0"
8026 sources."delegate-3.2.0"
8147 sources."delegate-3.2.0"
8027 sources."des.js-1.0.0"
8148 sources."des.js-1.0.1"
8028 sources."detect-file-1.0.0"
8149 sources."detect-file-1.0.0"
8029 sources."detect-indent-4.0.0"
8150 sources."detect-indent-4.0.0"
8030 sources."diffie-hellman-5.0.3"
8151 sources."diffie-hellman-5.0.3"
8031 sources."dir-glob-2.2.2"
8152 sources."dir-glob-2.2.2"
8032 sources."dom-converter-0.2.0"
8153 sources."dom-converter-0.2.0"
8033 (sources."dom-serializer-0.2.1" // {
8154 (sources."dom-serializer-0.2.2" // {
8034 dependencies = [
8155 dependencies = [
8035 sources."domelementtype-2.0.1"
8156 sources."domelementtype-2.0.1"
8036 sources."entities-2.0.0"
8157 sources."entities-2.0.0"
@@ -8046,33 +8167,33 b' let'
8046 sources."domelementtype-1.3.1"
8167 sources."domelementtype-1.3.1"
8047 sources."domhandler-2.3.0"
8168 sources."domhandler-2.3.0"
8048 sources."domutils-1.5.1"
8169 sources."domutils-1.5.1"
8049 sources."dropzone-5.5.1"
8170 sources."dropzone-5.7.0"
8050 sources."duplexer-0.1.1"
8171 sources."duplexer-0.1.1"
8051 (sources."duplexify-3.7.1" // {
8172 (sources."duplexify-3.7.1" // {
8052 dependencies = [
8173 dependencies = [
8053 sources."readable-stream-2.3.6"
8174 sources."readable-stream-2.3.7"
8054 sources."safe-buffer-5.1.2"
8175 sources."safe-buffer-5.1.2"
8055 sources."string_decoder-1.1.1"
8176 sources."string_decoder-1.1.1"
8056 ];
8177 ];
8057 })
8178 })
8058 sources."ecc-jsbn-0.1.2"
8179 sources."ecc-jsbn-0.1.2"
8059 sources."electron-to-chromium-1.3.302"
8180 sources."electron-to-chromium-1.3.412"
8060 sources."elliptic-6.5.1"
8181 sources."elliptic-6.5.2"
8061 sources."emojis-list-2.1.0"
8182 sources."emojis-list-3.0.0"
8062 sources."end-of-stream-1.4.4"
8183 sources."end-of-stream-1.4.4"
8063 (sources."enhanced-resolve-4.1.1" // {
8184 (sources."enhanced-resolve-4.1.1" // {
8064 dependencies = [
8185 dependencies = [
8065 sources."graceful-fs-4.2.3"
8186 sources."graceful-fs-4.2.3"
8066 sources."memory-fs-0.5.0"
8187 sources."memory-fs-0.5.0"
8067 sources."readable-stream-2.3.6"
8188 sources."readable-stream-2.3.7"
8068 sources."safe-buffer-5.1.2"
8189 sources."safe-buffer-5.1.2"
8069 sources."string_decoder-1.1.1"
8190 sources."string_decoder-1.1.1"
8070 ];
8191 ];
8071 })
8192 })
8072 sources."entities-1.0.0"
8193 sources."entities-1.0.0"
8073 sources."errno-0.1.7"
8194 sources."errno-0.1.7"
8074 sources."es-abstract-1.16.0"
8195 sources."es-abstract-1.17.5"
8075 sources."es-to-primitive-1.2.0"
8196 sources."es-to-primitive-1.2.1"
8076 sources."es6-templates-0.2.3"
8197 sources."es6-templates-0.2.3"
8077 sources."escape-string-regexp-1.0.5"
8198 sources."escape-string-regexp-1.0.5"
8078 sources."eslint-scope-4.0.3"
8199 sources."eslint-scope-4.0.3"
@@ -8082,7 +8203,7 b' let'
8082 sources."estraverse-4.3.0"
8203 sources."estraverse-4.3.0"
8083 sources."esutils-2.0.3"
8204 sources."esutils-2.0.3"
8084 sources."eventemitter2-0.4.14"
8205 sources."eventemitter2-0.4.14"
8085 sources."events-3.0.0"
8206 sources."events-3.1.0"
8086 sources."evp_bytestokey-1.0.3"
8207 sources."evp_bytestokey-1.0.3"
8087 sources."execa-1.0.0"
8208 sources."execa-1.0.0"
8088 sources."exit-0.1.2"
8209 sources."exit-0.1.2"
@@ -8119,13 +8240,14 b' let'
8119 ];
8240 ];
8120 })
8241 })
8121 sources."extsprintf-1.3.0"
8242 sources."extsprintf-1.3.0"
8122 sources."fast-deep-equal-2.0.1"
8243 sources."fast-deep-equal-3.1.1"
8123 sources."fast-json-stable-stringify-2.0.0"
8244 sources."fast-json-stable-stringify-2.1.0"
8124 sources."fastparse-1.1.2"
8245 sources."fastparse-1.1.2"
8125 sources."favico.js-0.3.10"
8246 sources."favico.js-0.3.10"
8126 sources."faye-websocket-0.4.4"
8247 sources."faye-websocket-0.4.4"
8127 sources."figures-1.7.0"
8248 sources."figures-1.7.0"
8128 sources."file-sync-cmp-0.1.1"
8249 sources."file-sync-cmp-0.1.1"
8250 sources."file-uri-to-path-1.0.0"
8129 (sources."fill-range-4.0.0" // {
8251 (sources."fill-range-4.0.0" // {
8130 dependencies = [
8252 dependencies = [
8131 sources."extend-shallow-2.0.1"
8253 sources."extend-shallow-2.0.1"
@@ -8145,7 +8267,7 b' let'
8145 sources."flatten-1.0.3"
8267 sources."flatten-1.0.3"
8146 (sources."flush-write-stream-1.1.1" // {
8268 (sources."flush-write-stream-1.1.1" // {
8147 dependencies = [
8269 dependencies = [
8148 sources."readable-stream-2.3.6"
8270 sources."readable-stream-2.3.7"
8149 sources."safe-buffer-5.1.2"
8271 sources."safe-buffer-5.1.2"
8150 sources."string_decoder-1.1.1"
8272 sources."string_decoder-1.1.1"
8151 ];
8273 ];
@@ -8157,7 +8279,7 b' let'
8157 sources."fragment-cache-0.2.1"
8279 sources."fragment-cache-0.2.1"
8158 (sources."from2-2.3.0" // {
8280 (sources."from2-2.3.0" // {
8159 dependencies = [
8281 dependencies = [
8160 sources."readable-stream-2.3.6"
8282 sources."readable-stream-2.3.7"
8161 sources."safe-buffer-5.1.2"
8283 sources."safe-buffer-5.1.2"
8162 sources."string_decoder-1.1.1"
8284 sources."string_decoder-1.1.1"
8163 ];
8285 ];
@@ -8168,7 +8290,7 b' let'
8168 ];
8290 ];
8169 })
8291 })
8170 sources."fs.realpath-1.0.0"
8292 sources."fs.realpath-1.0.0"
8171 sources."fsevents-1.2.9"
8293 sources."fsevents-1.2.12"
8172 sources."function-bind-1.1.1"
8294 sources."function-bind-1.1.1"
8173 sources."gaze-0.5.2"
8295 sources."gaze-0.5.2"
8174 sources."get-caller-file-1.0.3"
8296 sources."get-caller-file-1.0.3"
@@ -8200,7 +8322,7 b' let'
8200 sources."globals-9.18.0"
8322 sources."globals-9.18.0"
8201 (sources."globby-7.1.1" // {
8323 (sources."globby-7.1.1" // {
8202 dependencies = [
8324 dependencies = [
8203 sources."glob-7.1.5"
8325 sources."glob-7.1.6"
8204 sources."minimatch-3.0.4"
8326 sources."minimatch-3.0.4"
8205 ];
8327 ];
8206 })
8328 })
@@ -8214,7 +8336,7 b' let'
8214 sources."grunt-0.4.5"
8336 sources."grunt-0.4.5"
8215 (sources."grunt-cli-1.3.2" // {
8337 (sources."grunt-cli-1.3.2" // {
8216 dependencies = [
8338 dependencies = [
8217 sources."nopt-4.0.1"
8339 sources."nopt-4.0.3"
8218 ];
8340 ];
8219 })
8341 })
8220 (sources."grunt-contrib-concat-0.5.1" // {
8342 (sources."grunt-contrib-concat-0.5.1" // {
@@ -8289,7 +8411,7 b' let'
8289 sources."has-1.0.3"
8411 sources."has-1.0.3"
8290 sources."has-ansi-0.1.0"
8412 sources."has-ansi-0.1.0"
8291 sources."has-flag-3.0.0"
8413 sources."has-flag-3.0.0"
8292 sources."has-symbols-1.0.0"
8414 sources."has-symbols-1.0.1"
8293 sources."has-value-1.0.0"
8415 sources."has-value-1.0.0"
8294 (sources."has-values-1.0.0" // {
8416 (sources."has-values-1.0.0" // {
8295 dependencies = [
8417 dependencies = [
@@ -8321,6 +8443,7 b' let'
8321 (sources."html-webpack-plugin-3.2.0" // {
8443 (sources."html-webpack-plugin-3.2.0" // {
8322 dependencies = [
8444 dependencies = [
8323 sources."big.js-3.2.0"
8445 sources."big.js-3.2.0"
8446 sources."emojis-list-2.1.0"
8324 sources."json5-0.5.1"
8447 sources."json5-0.5.1"
8325 sources."loader-utils-0.2.17"
8448 sources."loader-utils-0.2.17"
8326 sources."lodash-4.17.15"
8449 sources."lodash-4.17.15"
@@ -8348,7 +8471,7 b' let'
8348 dependencies = [
8471 dependencies = [
8349 sources."find-up-3.0.0"
8472 sources."find-up-3.0.0"
8350 sources."locate-path-3.0.0"
8473 sources."locate-path-3.0.0"
8351 sources."p-limit-2.2.1"
8474 sources."p-limit-2.3.0"
8352 sources."p-locate-3.0.0"
8475 sources."p-locate-3.0.0"
8353 sources."p-try-2.2.0"
8476 sources."p-try-2.2.0"
8354 sources."pkg-dir-3.0.0"
8477 sources."pkg-dir-3.0.0"
@@ -8368,13 +8491,13 b' let'
8368 sources."is-accessor-descriptor-1.0.0"
8491 sources."is-accessor-descriptor-1.0.0"
8369 sources."is-binary-path-1.0.1"
8492 sources."is-binary-path-1.0.1"
8370 sources."is-buffer-1.1.6"
8493 sources."is-buffer-1.1.6"
8371 sources."is-callable-1.1.4"
8494 sources."is-callable-1.1.5"
8372 sources."is-data-descriptor-1.0.0"
8495 sources."is-data-descriptor-1.0.0"
8373 sources."is-date-object-1.0.1"
8496 sources."is-date-object-1.0.2"
8374 sources."is-descriptor-1.0.2"
8497 sources."is-descriptor-1.0.2"
8375 sources."is-extendable-0.1.1"
8498 sources."is-extendable-0.1.1"
8376 sources."is-extglob-2.1.1"
8499 sources."is-extglob-2.1.1"
8377 sources."is-finite-1.0.2"
8500 sources."is-finite-1.1.0"
8378 sources."is-fullwidth-code-point-1.0.0"
8501 sources."is-fullwidth-code-point-1.0.0"
8379 sources."is-glob-3.1.0"
8502 sources."is-glob-3.1.0"
8380 (sources."is-number-3.0.0" // {
8503 (sources."is-number-3.0.0" // {
@@ -8384,11 +8507,11 b' let'
8384 })
8507 })
8385 sources."is-plain-obj-1.1.0"
8508 sources."is-plain-obj-1.1.0"
8386 sources."is-plain-object-2.0.4"
8509 sources."is-plain-object-2.0.4"
8387 sources."is-regex-1.0.4"
8510 sources."is-regex-1.0.5"
8388 sources."is-relative-1.0.0"
8511 sources."is-relative-1.0.0"
8389 sources."is-stream-1.1.0"
8512 sources."is-stream-1.1.0"
8390 sources."is-svg-2.1.0"
8513 sources."is-svg-2.1.0"
8391 sources."is-symbol-1.0.2"
8514 sources."is-symbol-1.0.3"
8392 sources."is-typedarray-1.0.0"
8515 sources."is-typedarray-1.0.0"
8393 sources."is-unc-path-1.0.0"
8516 sources."is-unc-path-1.0.0"
8394 sources."is-windows-1.0.2"
8517 sources."is-windows-1.0.2"
@@ -8397,12 +8520,12 b' let'
8397 sources."isobject-3.0.1"
8520 sources."isobject-3.0.1"
8398 sources."isstream-0.1.2"
8521 sources."isstream-0.1.2"
8399 sources."jquery-1.11.3"
8522 sources."jquery-1.11.3"
8400 sources."js-base64-2.5.1"
8523 sources."js-base64-2.5.2"
8401 sources."js-tokens-3.0.2"
8524 sources."js-tokens-3.0.2"
8402 sources."js-yaml-2.0.5"
8525 sources."js-yaml-2.0.5"
8403 sources."jsbn-0.1.1"
8526 sources."jsbn-0.1.1"
8404 sources."jsesc-1.3.0"
8527 sources."jsesc-1.3.0"
8405 (sources."jshint-2.10.2" // {
8528 (sources."jshint-2.11.0" // {
8406 dependencies = [
8529 dependencies = [
8407 sources."lodash-4.17.15"
8530 sources."lodash-4.17.15"
8408 sources."minimatch-3.0.4"
8531 sources."minimatch-3.0.4"
@@ -8420,7 +8543,7 b' let'
8420 sources."assert-plus-1.0.0"
8543 sources."assert-plus-1.0.0"
8421 ];
8544 ];
8422 })
8545 })
8423 sources."kind-of-6.0.2"
8546 sources."kind-of-6.0.3"
8424 sources."lazy-cache-1.0.4"
8547 sources."lazy-cache-1.0.4"
8425 sources."lcid-2.0.0"
8548 sources."lcid-2.0.0"
8426 (sources."less-2.7.3" // {
8549 (sources."less-2.7.3" // {
@@ -8434,7 +8557,7 b' let'
8434 ];
8557 ];
8435 })
8558 })
8436 sources."loader-runner-2.4.0"
8559 sources."loader-runner-2.4.0"
8437 sources."loader-utils-1.2.3"
8560 sources."loader-utils-1.4.0"
8438 sources."locate-path-2.0.0"
8561 sources."locate-path-2.0.0"
8439 sources."lodash-0.9.2"
8562 sources."lodash-0.9.2"
8440 sources."lodash.camelcase-4.3.0"
8563 sources."lodash.camelcase-4.3.0"
@@ -8451,7 +8574,7 b' let'
8451 sources."map-cache-0.2.2"
8574 sources."map-cache-0.2.2"
8452 sources."map-visit-1.0.0"
8575 sources."map-visit-1.0.0"
8453 sources."mark.js-8.11.1"
8576 sources."mark.js-8.11.1"
8454 sources."math-expression-evaluator-1.2.17"
8577 sources."math-expression-evaluator-1.2.22"
8455 (sources."maxmin-2.1.0" // {
8578 (sources."maxmin-2.1.0" // {
8456 dependencies = [
8579 dependencies = [
8457 sources."ansi-regex-2.1.1"
8580 sources."ansi-regex-2.1.1"
@@ -8466,7 +8589,7 b' let'
8466 sources."mem-4.3.0"
8589 sources."mem-4.3.0"
8467 (sources."memory-fs-0.4.1" // {
8590 (sources."memory-fs-0.4.1" // {
8468 dependencies = [
8591 dependencies = [
8469 sources."readable-stream-2.3.6"
8592 sources."readable-stream-2.3.7"
8470 sources."safe-buffer-5.1.2"
8593 sources."safe-buffer-5.1.2"
8471 sources."string_decoder-1.1.1"
8594 sources."string_decoder-1.1.1"
8472 ];
8595 ];
@@ -8474,29 +8597,25 b' let'
8474 sources."micromatch-3.1.10"
8597 sources."micromatch-3.1.10"
8475 sources."miller-rabin-4.0.1"
8598 sources."miller-rabin-4.0.1"
8476 sources."mime-1.6.0"
8599 sources."mime-1.6.0"
8477 sources."mime-db-1.40.0"
8600 sources."mime-db-1.43.0"
8478 sources."mime-types-2.1.24"
8601 sources."mime-types-2.1.26"
8479 sources."mimic-fn-2.1.0"
8602 sources."mimic-fn-2.1.0"
8480 sources."minimalistic-assert-1.0.1"
8603 sources."minimalistic-assert-1.0.1"
8481 sources."minimalistic-crypto-utils-1.0.1"
8604 sources."minimalistic-crypto-utils-1.0.1"
8482 sources."minimatch-0.2.14"
8605 sources."minimatch-0.2.14"
8483 sources."minimist-1.2.0"
8606 sources."minimist-1.2.5"
8484 sources."mississippi-2.0.0"
8607 sources."mississippi-2.0.0"
8485 (sources."mixin-deep-1.3.2" // {
8608 (sources."mixin-deep-1.3.2" // {
8486 dependencies = [
8609 dependencies = [
8487 sources."is-extendable-1.0.1"
8610 sources."is-extendable-1.0.1"
8488 ];
8611 ];
8489 })
8612 })
8490 (sources."mkdirp-0.5.1" // {
8613 sources."mkdirp-0.5.5"
8491 dependencies = [
8492 sources."minimist-0.0.8"
8493 ];
8494 })
8495 sources."moment-2.24.0"
8614 sources."moment-2.24.0"
8496 sources."mousetrap-1.6.3"
8615 sources."mousetrap-1.6.5"
8497 (sources."move-concurrently-1.0.1" // {
8616 (sources."move-concurrently-1.0.1" // {
8498 dependencies = [
8617 dependencies = [
8499 sources."glob-7.1.5"
8618 sources."glob-7.1.6"
8500 sources."minimatch-3.0.4"
8619 sources."minimatch-3.0.4"
8501 sources."rimraf-2.7.1"
8620 sources."rimraf-2.7.1"
8502 ];
8621 ];
@@ -8509,7 +8628,7 b' let'
8509 sources."no-case-2.3.2"
8628 sources."no-case-2.3.2"
8510 (sources."node-libs-browser-2.2.1" // {
8629 (sources."node-libs-browser-2.2.1" // {
8511 dependencies = [
8630 dependencies = [
8512 (sources."readable-stream-2.3.6" // {
8631 (sources."readable-stream-2.3.7" // {
8513 dependencies = [
8632 dependencies = [
8514 sources."string_decoder-1.1.1"
8633 sources."string_decoder-1.1.1"
8515 ];
8634 ];
@@ -8550,11 +8669,12 b' let'
8550 sources."kind-of-3.2.2"
8669 sources."kind-of-3.2.2"
8551 ];
8670 ];
8552 })
8671 })
8553 sources."object-inspect-1.6.0"
8672 sources."object-inspect-1.7.0"
8554 sources."object-keys-1.1.1"
8673 sources."object-keys-1.1.1"
8555 sources."object-visit-1.0.1"
8674 sources."object-visit-1.0.1"
8675 sources."object.assign-4.1.0"
8556 sources."object.defaults-1.1.0"
8676 sources."object.defaults-1.1.0"
8557 sources."object.getownpropertydescriptors-2.0.3"
8677 sources."object.getownpropertydescriptors-2.1.0"
8558 sources."object.map-1.0.1"
8678 sources."object.map-1.0.1"
8559 sources."object.pick-1.3.0"
8679 sources."object.pick-1.3.0"
8560 sources."once-1.4.0"
8680 sources."once-1.4.0"
@@ -8569,10 +8689,10 b' let'
8569 sources."p-limit-1.3.0"
8689 sources."p-limit-1.3.0"
8570 sources."p-locate-2.0.0"
8690 sources."p-locate-2.0.0"
8571 sources."p-try-1.0.0"
8691 sources."p-try-1.0.0"
8572 sources."pako-1.0.10"
8692 sources."pako-1.0.11"
8573 (sources."parallel-transform-1.2.0" // {
8693 (sources."parallel-transform-1.2.0" // {
8574 dependencies = [
8694 dependencies = [
8575 sources."readable-stream-2.3.6"
8695 sources."readable-stream-2.3.7"
8576 sources."safe-buffer-5.1.2"
8696 sources."safe-buffer-5.1.2"
8577 sources."string_decoder-1.1.1"
8697 sources."string_decoder-1.1.1"
8578 ];
8698 ];
@@ -8729,7 +8849,7 b' let'
8729 (sources."readdirp-2.2.1" // {
8849 (sources."readdirp-2.2.1" // {
8730 dependencies = [
8850 dependencies = [
8731 sources."graceful-fs-4.2.3"
8851 sources."graceful-fs-4.2.3"
8732 sources."readable-stream-2.3.6"
8852 sources."readable-stream-2.3.7"
8733 sources."safe-buffer-5.1.2"
8853 sources."safe-buffer-5.1.2"
8734 sources."string_decoder-1.1.1"
8854 sources."string_decoder-1.1.1"
8735 ];
8855 ];
@@ -8747,6 +8867,7 b' let'
8747 })
8867 })
8748 sources."reduce-function-call-1.0.3"
8868 sources."reduce-function-call-1.0.3"
8749 sources."regenerate-1.4.0"
8869 sources."regenerate-1.4.0"
8870 sources."regenerate-unicode-properties-8.2.0"
8750 sources."regenerator-runtime-0.11.1"
8871 sources."regenerator-runtime-0.11.1"
8751 sources."regenerator-transform-0.10.1"
8872 sources."regenerator-transform-0.10.1"
8752 sources."regex-not-1.0.2"
8873 sources."regex-not-1.0.2"
@@ -8771,7 +8892,7 b' let'
8771 sources."request-2.81.0"
8892 sources."request-2.81.0"
8772 sources."require-directory-2.1.1"
8893 sources."require-directory-2.1.1"
8773 sources."require-main-filename-1.0.1"
8894 sources."require-main-filename-1.0.1"
8774 sources."resolve-1.12.0"
8895 sources."resolve-1.16.0"
8775 sources."resolve-cwd-2.0.0"
8896 sources."resolve-cwd-2.0.0"
8776 sources."resolve-dir-1.0.1"
8897 sources."resolve-dir-1.0.1"
8777 sources."resolve-from-3.0.0"
8898 sources."resolve-from-3.0.0"
@@ -8787,7 +8908,7 b' let'
8787 sources."sax-1.2.4"
8908 sources."sax-1.2.4"
8788 (sources."schema-utils-0.4.7" // {
8909 (sources."schema-utils-0.4.7" // {
8789 dependencies = [
8910 dependencies = [
8790 sources."ajv-6.10.2"
8911 sources."ajv-6.12.0"
8791 ];
8912 ];
8792 })
8913 })
8793 sources."select-1.1.2"
8914 sources."select-1.1.2"
@@ -8805,7 +8926,7 b' let'
8805 sources."shebang-regex-1.0.0"
8926 sources."shebang-regex-1.0.0"
8806 sources."shelljs-0.3.0"
8927 sources."shelljs-0.3.0"
8807 sources."sigmund-1.0.1"
8928 sources."sigmund-1.0.1"
8808 sources."signal-exit-3.0.2"
8929 sources."signal-exit-3.0.3"
8809 sources."slash-1.0.0"
8930 sources."slash-1.0.0"
8810 (sources."snapdragon-0.8.2" // {
8931 (sources."snapdragon-0.8.2" // {
8811 dependencies = [
8932 dependencies = [
@@ -8839,7 +8960,7 b' let'
8839 sources."sort-keys-1.1.2"
8960 sources."sort-keys-1.1.2"
8840 sources."source-list-map-2.0.1"
8961 sources."source-list-map-2.0.1"
8841 sources."source-map-0.5.7"
8962 sources."source-map-0.5.7"
8842 sources."source-map-resolve-0.5.2"
8963 sources."source-map-resolve-0.5.3"
8843 sources."source-map-support-0.4.18"
8964 sources."source-map-support-0.4.18"
8844 sources."source-map-url-0.4.0"
8965 sources."source-map-url-0.4.0"
8845 sources."split-string-3.1.0"
8966 sources."split-string-3.1.0"
@@ -8870,7 +8991,7 b' let'
8870 sources."sticky-sidebar-3.3.1"
8991 sources."sticky-sidebar-3.3.1"
8871 (sources."stream-browserify-2.0.2" // {
8992 (sources."stream-browserify-2.0.2" // {
8872 dependencies = [
8993 dependencies = [
8873 sources."readable-stream-2.3.6"
8994 sources."readable-stream-2.3.7"
8874 sources."safe-buffer-5.1.2"
8995 sources."safe-buffer-5.1.2"
8875 sources."string_decoder-1.1.1"
8996 sources."string_decoder-1.1.1"
8876 ];
8997 ];
@@ -8878,12 +8999,12 b' let'
8878 sources."stream-each-1.2.3"
8999 sources."stream-each-1.2.3"
8879 (sources."stream-http-2.8.3" // {
9000 (sources."stream-http-2.8.3" // {
8880 dependencies = [
9001 dependencies = [
8881 sources."readable-stream-2.3.6"
9002 sources."readable-stream-2.3.7"
8882 sources."safe-buffer-5.1.2"
9003 sources."safe-buffer-5.1.2"
8883 sources."string_decoder-1.1.1"
9004 sources."string_decoder-1.1.1"
8884 ];
9005 ];
8885 })
9006 })
8886 sources."stream-shift-1.0.0"
9007 sources."stream-shift-1.0.1"
8887 sources."strict-uri-encode-1.1.0"
9008 sources."strict-uri-encode-1.1.0"
8888 (sources."string-width-2.1.1" // {
9009 (sources."string-width-2.1.1" // {
8889 dependencies = [
9010 dependencies = [
@@ -8892,8 +9013,10 b' let'
8892 sources."strip-ansi-4.0.0"
9013 sources."strip-ansi-4.0.0"
8893 ];
9014 ];
8894 })
9015 })
8895 sources."string.prototype.trimleft-2.1.0"
9016 sources."string.prototype.trimend-1.0.1"
8896 sources."string.prototype.trimright-2.1.0"
9017 sources."string.prototype.trimleft-2.1.2"
9018 sources."string.prototype.trimright-2.1.2"
9019 sources."string.prototype.trimstart-1.0.1"
8897 sources."string_decoder-0.10.31"
9020 sources."string_decoder-0.10.31"
8898 sources."stringstream-0.0.6"
9021 sources."stringstream-0.0.6"
8899 sources."strip-ansi-0.3.0"
9022 sources."strip-ansi-0.3.0"
@@ -8909,11 +9032,12 b' let'
8909 sources."js-yaml-3.7.0"
9032 sources."js-yaml-3.7.0"
8910 ];
9033 ];
8911 })
9034 })
9035 sources."sweetalert2-9.10.12"
8912 sources."tapable-1.1.3"
9036 sources."tapable-1.1.3"
8913 sources."through-2.3.8"
9037 sources."through-2.3.8"
8914 (sources."through2-2.0.5" // {
9038 (sources."through2-2.0.5" // {
8915 dependencies = [
9039 dependencies = [
8916 sources."readable-stream-2.3.6"
9040 sources."readable-stream-2.3.7"
8917 sources."safe-buffer-5.1.2"
9041 sources."safe-buffer-5.1.2"
8918 sources."string_decoder-1.1.1"
9042 sources."string_decoder-1.1.1"
8919 ];
9043 ];
@@ -8942,6 +9066,7 b' let'
8942 dependencies = [
9066 dependencies = [
8943 sources."big.js-3.2.0"
9067 sources."big.js-3.2.0"
8944 sources."colors-1.4.0"
9068 sources."colors-1.4.0"
9069 sources."emojis-list-2.1.0"
8945 sources."enhanced-resolve-3.4.1"
9070 sources."enhanced-resolve-3.4.1"
8946 sources."graceful-fs-4.2.3"
9071 sources."graceful-fs-4.2.3"
8947 sources."json5-0.5.1"
9072 sources."json5-0.5.1"
@@ -8949,7 +9074,7 b' let'
8949 sources."tapable-0.2.9"
9074 sources."tapable-0.2.9"
8950 ];
9075 ];
8951 })
9076 })
8952 sources."tslib-1.10.0"
9077 sources."tslib-1.11.1"
8953 sources."tty-browserify-0.0.0"
9078 sources."tty-browserify-0.0.0"
8954 sources."tunnel-agent-0.6.0"
9079 sources."tunnel-agent-0.6.0"
8955 sources."tweetnacl-0.14.5"
9080 sources."tweetnacl-0.14.5"
@@ -8960,11 +9085,7 b' let'
8960 sources."source-map-0.6.1"
9085 sources."source-map-0.6.1"
8961 ];
9086 ];
8962 })
9087 })
8963 (sources."uglify-js-3.6.7" // {
9088 sources."uglify-js-3.9.1"
8964 dependencies = [
8965 sources."source-map-0.6.1"
8966 ];
8967 })
8968 sources."uglify-to-browserify-1.0.2"
9089 sources."uglify-to-browserify-1.0.2"
8969 (sources."uglifyjs-webpack-plugin-1.3.0" // {
9090 (sources."uglifyjs-webpack-plugin-1.3.0" // {
8970 dependencies = [
9091 dependencies = [
@@ -8974,6 +9095,10 b' let'
8974 sources."unc-path-regex-0.1.2"
9095 sources."unc-path-regex-0.1.2"
8975 sources."underscore-1.7.0"
9096 sources."underscore-1.7.0"
8976 sources."underscore.string-2.2.1"
9097 sources."underscore.string-2.2.1"
9098 sources."unicode-canonical-property-names-ecmascript-1.0.4"
9099 sources."unicode-match-property-ecmascript-1.0.4"
9100 sources."unicode-match-property-value-ecmascript-1.2.0"
9101 sources."unicode-property-aliases-ecmascript-1.1.0"
8977 sources."union-value-1.0.1"
9102 sources."union-value-1.0.1"
8978 sources."uniq-1.0.1"
9103 sources."uniq-1.0.1"
8979 sources."uniqs-2.0.0"
9104 sources."uniqs-2.0.0"
@@ -9012,17 +9137,17 b' let'
9012 sources."util-deprecate-1.0.2"
9137 sources."util-deprecate-1.0.2"
9013 sources."util.promisify-1.0.0"
9138 sources."util.promisify-1.0.0"
9014 sources."utila-0.4.0"
9139 sources."utila-0.4.0"
9015 sources."uuid-3.3.3"
9140 sources."uuid-3.4.0"
9016 sources."v8-compile-cache-2.1.0"
9141 sources."v8-compile-cache-2.1.0"
9017 sources."v8flags-3.1.3"
9142 sources."v8flags-3.1.3"
9018 sources."vendors-1.0.3"
9143 sources."vendors-1.0.4"
9019 (sources."verror-1.10.0" // {
9144 (sources."verror-1.10.0" // {
9020 dependencies = [
9145 dependencies = [
9021 sources."assert-plus-1.0.0"
9146 sources."assert-plus-1.0.0"
9022 ];
9147 ];
9023 })
9148 })
9024 sources."vm-browserify-1.1.0"
9149 sources."vm-browserify-1.1.2"
9025 (sources."watchpack-1.6.0" // {
9150 (sources."watchpack-1.6.1" // {
9026 dependencies = [
9151 dependencies = [
9027 sources."graceful-fs-4.2.3"
9152 sources."graceful-fs-4.2.3"
9028 ];
9153 ];
@@ -9030,7 +9155,7 b' let'
9030 sources."waypoints-4.0.1"
9155 sources."waypoints-4.0.1"
9031 (sources."webpack-4.23.1" // {
9156 (sources."webpack-4.23.1" // {
9032 dependencies = [
9157 dependencies = [
9033 sources."ajv-6.10.2"
9158 sources."ajv-6.12.0"
9034 ];
9159 ];
9035 })
9160 })
9036 (sources."webpack-cli-3.1.2" // {
9161 (sources."webpack-cli-3.1.2" // {
@@ -9086,7 +9211,7 b' let'
9086 dependencies = [
9211 dependencies = [
9087 sources."find-up-3.0.0"
9212 sources."find-up-3.0.0"
9088 sources."locate-path-3.0.0"
9213 sources."locate-path-3.0.0"
9089 sources."p-limit-2.2.1"
9214 sources."p-limit-2.3.0"
9090 sources."p-locate-3.0.0"
9215 sources."p-locate-3.0.0"
9091 sources."p-try-2.2.0"
9216 sources."p-try-2.2.0"
9092 ];
9217 ];
@@ -5,7 +5,7 b''
5
5
6 self: super: {
6 self: super: {
7 "alembic" = super.buildPythonPackage {
7 "alembic" = super.buildPythonPackage {
8 name = "alembic-1.3.1";
8 name = "alembic-1.4.2";
9 doCheck = false;
9 doCheck = false;
10 propagatedBuildInputs = [
10 propagatedBuildInputs = [
11 self."sqlalchemy"
11 self."sqlalchemy"
@@ -14,8 +14,8 b' self: super: {'
14 self."python-dateutil"
14 self."python-dateutil"
15 ];
15 ];
16 src = fetchurl {
16 src = fetchurl {
17 url = "https://files.pythonhosted.org/packages/84/64/493c45119dce700a4b9eeecc436ef9e8835ab67bae6414f040cdc7b58f4b/alembic-1.3.1.tar.gz";
17 url = "https://files.pythonhosted.org/packages/60/1e/cabc75a189de0fbb2841d0975243e59bde8b7822bacbb95008ac6fe9ad47/alembic-1.4.2.tar.gz";
18 sha256 = "1cl2chk5jx0rf4hmsd5lljic7iifw17yv3y5xawvp4i14jvpn9s9";
18 sha256 = "1gsdrzx9h7wfva200qvvsc9sn4w79mk2vs0bbnzjhxi1jw2b0nh3";
19 };
19 };
20 meta = {
20 meta = {
21 license = [ pkgs.lib.licenses.mit ];
21 license = [ pkgs.lib.licenses.mit ];
@@ -146,15 +146,15 b' self: super: {'
146 };
146 };
147 };
147 };
148 "bleach" = super.buildPythonPackage {
148 "bleach" = super.buildPythonPackage {
149 name = "bleach-3.1.0";
149 name = "bleach-3.1.3";
150 doCheck = false;
150 doCheck = false;
151 propagatedBuildInputs = [
151 propagatedBuildInputs = [
152 self."six"
152 self."six"
153 self."webencodings"
153 self."webencodings"
154 ];
154 ];
155 src = fetchurl {
155 src = fetchurl {
156 url = "https://files.pythonhosted.org/packages/78/5a/0df03e8735cd9c75167528299c738702437589b9c71a849489d00ffa82e8/bleach-3.1.0.tar.gz";
156 url = "https://files.pythonhosted.org/packages/de/09/5267f8577a92487ed43bc694476c4629c6eca2e3c93fcf690a26bfe39e1d/bleach-3.1.3.tar.gz";
157 sha256 = "1yhrgrhkln8bd6gn3imj69g1h4xqah9gaz9q26crqr6gmmvpzprz";
157 sha256 = "0al437aw4p2xp83az5hhlrp913nsf0cg6kg4qj3fjhv4wakxipzq";
158 };
158 };
159 meta = {
159 meta = {
160 license = [ pkgs.lib.licenses.asl20 ];
160 license = [ pkgs.lib.licenses.asl20 ];
@@ -171,6 +171,17 b' self: super: {'
171 license = [ pkgs.lib.licenses.mit ];
171 license = [ pkgs.lib.licenses.mit ];
172 };
172 };
173 };
173 };
174 "cachetools" = super.buildPythonPackage {
175 name = "cachetools-3.1.1";
176 doCheck = false;
177 src = fetchurl {
178 url = "https://files.pythonhosted.org/packages/ae/37/7fd45996b19200e0cb2027a0b6bef4636951c4ea111bfad36c71287247f6/cachetools-3.1.1.tar.gz";
179 sha256 = "16m69l6n6y1r1y7cklm92rr7v69ldig2n3lbl3j323w5jz7d78lf";
180 };
181 meta = {
182 license = [ pkgs.lib.licenses.mit ];
183 };
184 };
174 "celery" = super.buildPythonPackage {
185 "celery" = super.buildPythonPackage {
175 name = "celery-4.3.0";
186 name = "celery-4.3.0";
176 doCheck = false;
187 doCheck = false;
@@ -189,11 +200,11 b' self: super: {'
189 };
200 };
190 };
201 };
191 "certifi" = super.buildPythonPackage {
202 "certifi" = super.buildPythonPackage {
192 name = "certifi-2019.11.28";
203 name = "certifi-2020.4.5.1";
193 doCheck = false;
204 doCheck = false;
194 src = fetchurl {
205 src = fetchurl {
195 url = "https://files.pythonhosted.org/packages/41/bf/9d214a5af07debc6acf7f3f257265618f1db242a3f8e49a9b516f24523a6/certifi-2019.11.28.tar.gz";
206 url = "https://files.pythonhosted.org/packages/b8/e2/a3a86a67c3fc8249ed305fc7b7d290ebe5e4d46ad45573884761ef4dea7b/certifi-2020.4.5.1.tar.gz";
196 sha256 = "07qg6864bk4qxa8akr967amlmsq9v310hp039mcpjx6dliylrdi5";
207 sha256 = "06b5gfs7wmmipln8f3z928d2mmx2j4b3x7pnqmj6cvmyfh8v7z2i";
197 };
208 };
198 meta = {
209 meta = {
199 license = [ pkgs.lib.licenses.mpl20 { fullName = "Mozilla Public License 2.0 (MPL 2.0)"; } ];
210 license = [ pkgs.lib.licenses.mpl20 { fullName = "Mozilla Public License 2.0 (MPL 2.0)"; } ];
@@ -372,6 +383,17 b' self: super: {'
372 license = [ pkgs.lib.licenses.bsdOriginal ];
383 license = [ pkgs.lib.licenses.bsdOriginal ];
373 };
384 };
374 };
385 };
386 "cssutils" = super.buildPythonPackage {
387 name = "cssutils-1.0.2";
388 doCheck = false;
389 src = fetchurl {
390 url = "https://files.pythonhosted.org/packages/5c/0b/c5f29d29c037e97043770b5e7c740b6252993e4b57f029b3cd03c78ddfec/cssutils-1.0.2.tar.gz";
391 sha256 = "1bxchrbqzapwijap0yhlxdil1w9bmwvgx77aizlkhc2mcxjg1z52";
392 };
393 meta = {
394 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL 2.1 or later, see also http://cthedot.de/cssutils/"; } ];
395 };
396 };
375 "decorator" = super.buildPythonPackage {
397 "decorator" = super.buildPythonPackage {
376 name = "decorator-4.1.2";
398 name = "decorator-4.1.2";
377 doCheck = false;
399 doCheck = false;
@@ -429,11 +451,11 b' self: super: {'
429 };
451 };
430 };
452 };
431 "docutils" = super.buildPythonPackage {
453 "docutils" = super.buildPythonPackage {
432 name = "docutils-0.14";
454 name = "docutils-0.16";
433 doCheck = false;
455 doCheck = false;
434 src = fetchurl {
456 src = fetchurl {
435 url = "https://files.pythonhosted.org/packages/84/f4/5771e41fdf52aabebbadecc9381d11dea0fa34e4759b4071244fa094804c/docutils-0.14.tar.gz";
457 url = "https://files.pythonhosted.org/packages/2f/e0/3d435b34abd2d62e8206171892f174b180cd37b09d57b924ca5c2ef2219d/docutils-0.16.tar.gz";
436 sha256 = "0x22fs3pdmr42kvz6c654756wja305qv6cx1zbhwlagvxgr4xrji";
458 sha256 = "1z3qliszqca9m719q3qhdkh0ghh90g500avzdgi7pl77x5h3mpn2";
437 };
459 };
438 meta = {
460 meta = {
439 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
461 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
@@ -565,11 +587,11 b' self: super: {'
565 };
587 };
566 };
588 };
567 "enum34" = super.buildPythonPackage {
589 "enum34" = super.buildPythonPackage {
568 name = "enum34-1.1.6";
590 name = "enum34-1.1.10";
569 doCheck = false;
591 doCheck = false;
570 src = fetchurl {
592 src = fetchurl {
571 url = "https://files.pythonhosted.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
593 url = "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz";
572 sha256 = "1cgm5ng2gcfrkrm3hc22brl6chdmv67b9zvva9sfs7gn7dwc9n4a";
594 sha256 = "0j7ji699fwswm4vg6w1v07fkbf8dkzdm6gfh88jvs5nqgr3sgrnc";
573 };
595 };
574 meta = {
596 meta = {
575 license = [ pkgs.lib.licenses.bsdOriginal ];
597 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -631,14 +653,14 b' self: super: {'
631 };
653 };
632 };
654 };
633 "gevent" = super.buildPythonPackage {
655 "gevent" = super.buildPythonPackage {
634 name = "gevent-1.4.0";
656 name = "gevent-1.5.0";
635 doCheck = false;
657 doCheck = false;
636 propagatedBuildInputs = [
658 propagatedBuildInputs = [
637 self."greenlet"
659 self."greenlet"
638 ];
660 ];
639 src = fetchurl {
661 src = fetchurl {
640 url = "https://files.pythonhosted.org/packages/ed/27/6c49b70808f569b66ec7fac2e78f076e9b204db9cf5768740cff3d5a07ae/gevent-1.4.0.tar.gz";
662 url = "https://files.pythonhosted.org/packages/5a/79/2c63d385d017b5dd7d70983a463dfd25befae70c824fedb857df6e72eff2/gevent-1.5.0.tar.gz";
641 sha256 = "1lchr4akw2jkm5v4kz7bdm4wv3knkfhbfn9vkkz4s5yrkcxzmdqy";
663 sha256 = "0aac3d4vhv5n4rsb6cqzq0d1xx9immqz4fmpddw35yxkwdc450dj";
642 };
664 };
643 meta = {
665 meta = {
644 license = [ pkgs.lib.licenses.mit ];
666 license = [ pkgs.lib.licenses.mit ];
@@ -689,11 +711,11 b' self: super: {'
689 };
711 };
690 };
712 };
691 "hupper" = super.buildPythonPackage {
713 "hupper" = super.buildPythonPackage {
692 name = "hupper-1.9.1";
714 name = "hupper-1.10.2";
693 doCheck = false;
715 doCheck = false;
694 src = fetchurl {
716 src = fetchurl {
695 url = "https://files.pythonhosted.org/packages/09/3a/4f215659f31eeffe364a984dba486bfa3907bfcc54b7013bdfe825cebb5f/hupper-1.9.1.tar.gz";
717 url = "https://files.pythonhosted.org/packages/41/24/ea90fef04706e54bd1635c05c50dc9cf87cda543c59303a03e7aa7dda0ce/hupper-1.10.2.tar.gz";
696 sha256 = "0pyg879fv9mbwlnbzw2a3234qqycqs9l97h5mpkmk0bvxhi2471v";
718 sha256 = "0am0p6g5cz6xmcaf04xq8q6dzdd9qz0phj6gcmpsckf2mcyza61q";
697 };
719 };
698 meta = {
720 meta = {
699 license = [ pkgs.lib.licenses.mit ];
721 license = [ pkgs.lib.licenses.mit ];
@@ -711,17 +733,17 b' self: super: {'
711 };
733 };
712 };
734 };
713 "importlib-metadata" = super.buildPythonPackage {
735 "importlib-metadata" = super.buildPythonPackage {
714 name = "importlib-metadata-0.23";
736 name = "importlib-metadata-1.6.0";
715 doCheck = false;
737 doCheck = false;
716 propagatedBuildInputs = [
738 propagatedBuildInputs = [
717 self."zipp"
739 self."zipp"
740 self."pathlib2"
718 self."contextlib2"
741 self."contextlib2"
719 self."configparser"
742 self."configparser"
720 self."pathlib2"
721 ];
743 ];
722 src = fetchurl {
744 src = fetchurl {
723 url = "https://files.pythonhosted.org/packages/5d/44/636bcd15697791943e2dedda0dbe098d8530a38d113b202817133e0b06c0/importlib_metadata-0.23.tar.gz";
745 url = "https://files.pythonhosted.org/packages/b4/1b/baab42e3cd64c9d5caac25a9d6c054f8324cdc38975a44d600569f1f7158/importlib_metadata-1.6.0.tar.gz";
724 sha256 = "09mdqdfv5rdrwz80jh9m379gxmvk2vhjfz0fg53hid00icvxf65a";
746 sha256 = "07icyggasn38yv2swdrd8z6i0plazmc9adavsdkbqqj91j53ll9l";
725 };
747 };
726 meta = {
748 meta = {
727 license = [ pkgs.lib.licenses.asl20 ];
749 license = [ pkgs.lib.licenses.asl20 ];
@@ -765,15 +787,15 b' self: super: {'
765 };
787 };
766 };
788 };
767 "ipdb" = super.buildPythonPackage {
789 "ipdb" = super.buildPythonPackage {
768 name = "ipdb-0.12";
790 name = "ipdb-0.13.2";
769 doCheck = false;
791 doCheck = false;
770 propagatedBuildInputs = [
792 propagatedBuildInputs = [
771 self."setuptools"
793 self."setuptools"
772 self."ipython"
794 self."ipython"
773 ];
795 ];
774 src = fetchurl {
796 src = fetchurl {
775 url = "https://files.pythonhosted.org/packages/6d/43/c3c2e866a8803e196d6209595020a4a6db1a3c5d07c01455669497ae23d0/ipdb-0.12.tar.gz";
797 url = "https://files.pythonhosted.org/packages/2c/bb/a3e1a441719ebd75c6dac8170d3ddba884b7ee8a5c0f9aefa7297386627a/ipdb-0.13.2.tar.gz";
776 sha256 = "1khr2n7xfy8hg65kj1bsrjq9g7656pp0ybfa8abpbzpdawji3qnw";
798 sha256 = "0jcd849rx30y3wcgzsqbn06v0yjlzvb9x3076q0yxpycdwm1ryvp";
777 };
799 };
778 meta = {
800 meta = {
779 license = [ pkgs.lib.licenses.bsdOriginal ];
801 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -1074,15 +1096,15 b' self: super: {'
1074 };
1096 };
1075 };
1097 };
1076 "packaging" = super.buildPythonPackage {
1098 "packaging" = super.buildPythonPackage {
1077 name = "packaging-19.2";
1099 name = "packaging-20.3";
1078 doCheck = false;
1100 doCheck = false;
1079 propagatedBuildInputs = [
1101 propagatedBuildInputs = [
1080 self."pyparsing"
1102 self."pyparsing"
1081 self."six"
1103 self."six"
1082 ];
1104 ];
1083 src = fetchurl {
1105 src = fetchurl {
1084 url = "https://files.pythonhosted.org/packages/5a/2f/449ded84226d0e2fda8da9252e5ee7731bdf14cd338f622dfcd9934e0377/packaging-19.2.tar.gz";
1106 url = "https://files.pythonhosted.org/packages/65/37/83e3f492eb52d771e2820e88105f605335553fe10422cba9d256faeb1702/packaging-20.3.tar.gz";
1085 sha256 = "0izwlz9h0bw171a1chr311g2y7n657zjaf4mq4rgm8pp9lbj9f98";
1107 sha256 = "18xpablq278janh03bai9xd4kz9b0yfp6vflazn725ns9x3jna9w";
1086 };
1108 };
1087 meta = {
1109 meta = {
1088 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
1110 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
@@ -1100,25 +1122,25 b' self: super: {'
1100 };
1122 };
1101 };
1123 };
1102 "paste" = super.buildPythonPackage {
1124 "paste" = super.buildPythonPackage {
1103 name = "paste-3.2.1";
1125 name = "paste-3.4.0";
1104 doCheck = false;
1126 doCheck = false;
1105 propagatedBuildInputs = [
1127 propagatedBuildInputs = [
1106 self."six"
1128 self."six"
1107 ];
1129 ];
1108 src = fetchurl {
1130 src = fetchurl {
1109 url = "https://files.pythonhosted.org/packages/0d/86/7008b5563594e8a63763f05212a3eb84c85f0b2eff834e5697716e56bca9/Paste-3.2.1.tar.gz";
1131 url = "https://files.pythonhosted.org/packages/79/4a/45821b71dd40000507549afd1491546afad8279c0a87527c88776a794158/Paste-3.4.0.tar.gz";
1110 sha256 = "1vjxr8n1p31c9x9rh8g0f34yisa9028cxpvn36q7g1s0m2b9x71x";
1132 sha256 = "16sichvhyci1gaarkjs35mai8vphh7b244qm14hj1isw38nx4c03";
1111 };
1133 };
1112 meta = {
1134 meta = {
1113 license = [ pkgs.lib.licenses.mit ];
1135 license = [ pkgs.lib.licenses.mit ];
1114 };
1136 };
1115 };
1137 };
1116 "pastedeploy" = super.buildPythonPackage {
1138 "pastedeploy" = super.buildPythonPackage {
1117 name = "pastedeploy-2.0.1";
1139 name = "pastedeploy-2.1.0";
1118 doCheck = false;
1140 doCheck = false;
1119 src = fetchurl {
1141 src = fetchurl {
1120 url = "https://files.pythonhosted.org/packages/19/a0/5623701df7e2478a68a1b685d1a84518024eef994cde7e4da8449a31616f/PasteDeploy-2.0.1.tar.gz";
1142 url = "https://files.pythonhosted.org/packages/c4/e9/972a1c20318b3ae9edcab11a6cef64308fbae5d0d45ab52c6f8b2b8f35b8/PasteDeploy-2.1.0.tar.gz";
1121 sha256 = "02imfbbx1mi2h546f3sr37m47dk9qizaqhzzlhx8bkzxa6fzn8yl";
1143 sha256 = "16qsq5y6mryslmbp5pn35x4z8z3ndp5rpgl42h226879nrw9hmg7";
1122 };
1144 };
1123 meta = {
1145 meta = {
1124 license = [ pkgs.lib.licenses.mit ];
1146 license = [ pkgs.lib.licenses.mit ];
@@ -1167,14 +1189,14 b' self: super: {'
1167 };
1189 };
1168 };
1190 };
1169 "pexpect" = super.buildPythonPackage {
1191 "pexpect" = super.buildPythonPackage {
1170 name = "pexpect-4.7.0";
1192 name = "pexpect-4.8.0";
1171 doCheck = false;
1193 doCheck = false;
1172 propagatedBuildInputs = [
1194 propagatedBuildInputs = [
1173 self."ptyprocess"
1195 self."ptyprocess"
1174 ];
1196 ];
1175 src = fetchurl {
1197 src = fetchurl {
1176 url = "https://files.pythonhosted.org/packages/1c/b1/362a0d4235496cb42c33d1d8732b5e2c607b0129ad5fdd76f5a583b9fcb3/pexpect-4.7.0.tar.gz";
1198 url = "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz";
1177 sha256 = "1sv2rri15zwhds85a4kamwh9pj49qcxv7m4miyr4jfpfwv81yb4y";
1199 sha256 = "032cg337h8awydgypz6f4wx848lw8dyrj4zy988x0lyib4ws8rgw";
1178 };
1200 };
1179 meta = {
1201 meta = {
1180 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1202 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
@@ -1237,6 +1259,24 b' self: super: {'
1237 license = [ pkgs.lib.licenses.mit ];
1259 license = [ pkgs.lib.licenses.mit ];
1238 };
1260 };
1239 };
1261 };
1262 "premailer" = super.buildPythonPackage {
1263 name = "premailer-3.6.1";
1264 doCheck = false;
1265 propagatedBuildInputs = [
1266 self."lxml"
1267 self."cssselect"
1268 self."cssutils"
1269 self."requests"
1270 self."cachetools"
1271 ];
1272 src = fetchurl {
1273 url = "https://files.pythonhosted.org/packages/62/da/2f43cdf9d3d79c80c4856a12389a1f257d65fe9ccc44bc6b4383c8a18e33/premailer-3.6.1.tar.gz";
1274 sha256 = "08pshx7a110k4ll20x0xhpvyn3kkipkrbgxjjn7ncdxs54ihdhgw";
1275 };
1276 meta = {
1277 license = [ pkgs.lib.licenses.psfl { fullName = "Python"; } ];
1278 };
1279 };
1240 "prompt-toolkit" = super.buildPythonPackage {
1280 "prompt-toolkit" = super.buildPythonPackage {
1241 name = "prompt-toolkit-1.0.18";
1281 name = "prompt-toolkit-1.0.18";
1242 doCheck = false;
1282 doCheck = false;
@@ -1253,11 +1293,11 b' self: super: {'
1253 };
1293 };
1254 };
1294 };
1255 "psutil" = super.buildPythonPackage {
1295 "psutil" = super.buildPythonPackage {
1256 name = "psutil-5.6.5";
1296 name = "psutil-5.7.0";
1257 doCheck = false;
1297 doCheck = false;
1258 src = fetchurl {
1298 src = fetchurl {
1259 url = "https://files.pythonhosted.org/packages/03/9a/95c4b3d0424426e5fd94b5302ff74cea44d5d4f53466e1228ac8e73e14b4/psutil-5.6.5.tar.gz";
1299 url = "https://files.pythonhosted.org/packages/c4/b8/3512f0e93e0db23a71d82485ba256071ebef99b227351f0f5540f744af41/psutil-5.7.0.tar.gz";
1260 sha256 = "0isil5jxwwd8awz54qk28rpgjg43i5l6yl70g40vxwa4r4m56lfh";
1300 sha256 = "03jykdi3dgf1cdal9bv4fq9zjvzj9l9bs99gi5ar81sdl5nc2pk8";
1261 };
1301 };
1262 meta = {
1302 meta = {
1263 license = [ pkgs.lib.licenses.bsdOriginal ];
1303 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -1348,11 +1388,11 b' self: super: {'
1348 };
1388 };
1349 };
1389 };
1350 "pycparser" = super.buildPythonPackage {
1390 "pycparser" = super.buildPythonPackage {
1351 name = "pycparser-2.19";
1391 name = "pycparser-2.20";
1352 doCheck = false;
1392 doCheck = false;
1353 src = fetchurl {
1393 src = fetchurl {
1354 url = "https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz";
1394 url = "https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz";
1355 sha256 = "1cr5dcj9628lkz1qlwq3fv97c25363qppkmcayqvd05dpy573259";
1395 sha256 = "1w0m3xvlrzq4lkbvd1ngfm8mdw64r1yxy6n7djlw6qj5d0km6ird";
1356 };
1396 };
1357 meta = {
1397 meta = {
1358 license = [ pkgs.lib.licenses.bsdOriginal ];
1398 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -1414,11 +1454,11 b' self: super: {'
1414 };
1454 };
1415 };
1455 };
1416 "pyparsing" = super.buildPythonPackage {
1456 "pyparsing" = super.buildPythonPackage {
1417 name = "pyparsing-2.4.5";
1457 name = "pyparsing-2.4.7";
1418 doCheck = false;
1458 doCheck = false;
1419 src = fetchurl {
1459 src = fetchurl {
1420 url = "https://files.pythonhosted.org/packages/00/32/8076fa13e832bb4dcff379f18f228e5a53412be0631808b9ca2610c0f566/pyparsing-2.4.5.tar.gz";
1460 url = "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz";
1421 sha256 = "0fk8gsybiw1gm146mkjdjvaajwh20xwvpv4j7syh2zrnpq0j19jc";
1461 sha256 = "1hgc8qrbq1ymxbwfbjghv01fm3fbpjwpjwi0bcailxxzhf3yq0y2";
1422 };
1462 };
1423 meta = {
1463 meta = {
1424 license = [ pkgs.lib.licenses.mit ];
1464 license = [ pkgs.lib.licenses.mit ];
@@ -1448,7 +1488,7 b' self: super: {'
1448 };
1488 };
1449 };
1489 };
1450 "pyramid-debugtoolbar" = super.buildPythonPackage {
1490 "pyramid-debugtoolbar" = super.buildPythonPackage {
1451 name = "pyramid-debugtoolbar-4.5.1";
1491 name = "pyramid-debugtoolbar-4.6.1";
1452 doCheck = false;
1492 doCheck = false;
1453 propagatedBuildInputs = [
1493 propagatedBuildInputs = [
1454 self."pyramid"
1494 self."pyramid"
@@ -1458,8 +1498,8 b' self: super: {'
1458 self."ipaddress"
1498 self."ipaddress"
1459 ];
1499 ];
1460 src = fetchurl {
1500 src = fetchurl {
1461 url = "https://files.pythonhosted.org/packages/88/21/74e7fa52edc74667e29403bd0cb4f2bb74dc4014711de313868001bf639f/pyramid_debugtoolbar-4.5.1.tar.gz";
1501 url = "https://files.pythonhosted.org/packages/99/f6/b8603f82c18275be293921bc3a2184205056ca505747bf64ab8a0c08e124/pyramid_debugtoolbar-4.6.1.tar.gz";
1462 sha256 = "0hgf6i1fzvq43m9vjdmb24nnv8fwp7sdzrx9bcwrgpy24n07am9a";
1502 sha256 = "185z7q8n959ga5331iczwra2iljwkidfx4qn6bbd7vm3rm4w6llv";
1463 };
1503 };
1464 meta = {
1504 meta = {
1465 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1505 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
@@ -1646,15 +1686,15 b' self: super: {'
1646 };
1686 };
1647 };
1687 };
1648 "python-ldap" = super.buildPythonPackage {
1688 "python-ldap" = super.buildPythonPackage {
1649 name = "python-ldap-3.1.0";
1689 name = "python-ldap-3.2.0";
1650 doCheck = false;
1690 doCheck = false;
1651 propagatedBuildInputs = [
1691 propagatedBuildInputs = [
1652 self."pyasn1"
1692 self."pyasn1"
1653 self."pyasn1-modules"
1693 self."pyasn1-modules"
1654 ];
1694 ];
1655 src = fetchurl {
1695 src = fetchurl {
1656 url = "https://files.pythonhosted.org/packages/7f/1c/28d721dff2fcd2fef9d55b40df63a00be26ec8a11e8c6fc612ae642f9cfd/python-ldap-3.1.0.tar.gz";
1696 url = "https://files.pythonhosted.org/packages/ea/93/596f875e003c770447f4b99267820a0c769dd2dc3ae3ed19afe460fcbad0/python-ldap-3.2.0.tar.gz";
1657 sha256 = "1i97nwfnraylyn0myxlf3vciicrf5h6fymrcff9c00k581wmx5s1";
1697 sha256 = "13nvrhp85yr0jyxixcjj012iw8l9wynxxlykm9j3alss6waln73x";
1658 };
1698 };
1659 meta = {
1699 meta = {
1660 license = [ pkgs.lib.licenses.psfl ];
1700 license = [ pkgs.lib.licenses.psfl ];
@@ -1702,11 +1742,11 b' self: super: {'
1702 };
1742 };
1703 };
1743 };
1704 "pytz" = super.buildPythonPackage {
1744 "pytz" = super.buildPythonPackage {
1705 name = "pytz-2019.2";
1745 name = "pytz-2019.3";
1706 doCheck = false;
1746 doCheck = false;
1707 src = fetchurl {
1747 src = fetchurl {
1708 url = "https://files.pythonhosted.org/packages/27/c0/fbd352ca76050952a03db776d241959d5a2ee1abddfeb9e2a53fdb489be4/pytz-2019.2.tar.gz";
1748 url = "https://files.pythonhosted.org/packages/82/c3/534ddba230bd4fbbd3b7a3d35f3341d014cca213f369a9940925e7e5f691/pytz-2019.3.tar.gz";
1709 sha256 = "0ckb27hhjc8i8gcdvk4d9avld62b7k52yjijc60s2m3y8cpb7h16";
1749 sha256 = "1ghrk1wg45d3nymj7bf4zj03n3bh64xmczhk4pfi577hdkdhcb5h";
1710 };
1750 };
1711 meta = {
1751 meta = {
1712 license = [ pkgs.lib.licenses.mit ];
1752 license = [ pkgs.lib.licenses.mit ];
@@ -1724,11 +1764,11 b' self: super: {'
1724 };
1764 };
1725 };
1765 };
1726 "redis" = super.buildPythonPackage {
1766 "redis" = super.buildPythonPackage {
1727 name = "redis-3.3.11";
1767 name = "redis-3.4.1";
1728 doCheck = false;
1768 doCheck = false;
1729 src = fetchurl {
1769 src = fetchurl {
1730 url = "https://files.pythonhosted.org/packages/06/ca/00557c74279d2f256d3c42cabf237631355f3a132e4c74c2000e6647ad98/redis-3.3.11.tar.gz";
1770 url = "https://files.pythonhosted.org/packages/ef/2e/2c0f59891db7db087a7eeaa79bc7c7f2c039e71a2b5b0a41391e9d462926/redis-3.4.1.tar.gz";
1731 sha256 = "1hicqbi5xl92hhml82awrr2rxl9jar5fp8nbcycj9qgmsdwc43wd";
1771 sha256 = "07yaj0j9fs7xdkg5bg926fa990khyigjbp31si8ai20vj8sv7kqd";
1732 };
1772 };
1733 meta = {
1773 meta = {
1734 license = [ pkgs.lib.licenses.mit ];
1774 license = [ pkgs.lib.licenses.mit ];
@@ -1779,7 +1819,7 b' self: super: {'
1779 };
1819 };
1780 };
1820 };
1781 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1821 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1782 name = "rhodecode-enterprise-ce-4.18.3";
1822 name = "rhodecode-enterprise-ce-4.19.0";
1783 buildInputs = [
1823 buildInputs = [
1784 self."pytest"
1824 self."pytest"
1785 self."py"
1825 self."py"
@@ -1833,6 +1873,7 b' self: super: {'
1833 self."pastedeploy"
1873 self."pastedeploy"
1834 self."pastescript"
1874 self."pastescript"
1835 self."peppercorn"
1875 self."peppercorn"
1876 self."premailer"
1836 self."psutil"
1877 self."psutil"
1837 self."py-bcrypt"
1878 self."py-bcrypt"
1838 self."pycurl"
1879 self."pycurl"
@@ -1976,11 +2017,11 b' self: super: {'
1976 };
2017 };
1977 };
2018 };
1978 "setuptools" = super.buildPythonPackage {
2019 "setuptools" = super.buildPythonPackage {
1979 name = "setuptools-44.0.0";
2020 name = "setuptools-44.1.0";
1980 doCheck = false;
2021 doCheck = false;
1981 src = fetchurl {
2022 src = fetchurl {
1982 url = "https://files.pythonhosted.org/packages/b0/f3/44da7482ac6da3f36f68e253cb04de37365b3dba9036a3c70773b778b485/setuptools-44.0.0.zip";
2023 url = "https://files.pythonhosted.org/packages/ed/7b/bbf89ca71e722b7f9464ebffe4b5ee20a9e5c9a555a56e2d3914bb9119a6/setuptools-44.1.0.zip";
1983 sha256 = "025h5cnxcmda1893l6i12hrwdvs1n8r31qs6q4pkif2v7rrggfp5";
2024 sha256 = "1jja896zvd1ppccnjbhkgagxbwchgq6vfamp6qn1hvywq6q9cjkr";
1984 };
2025 };
1985 meta = {
2026 meta = {
1986 license = [ pkgs.lib.licenses.mit ];
2027 license = [ pkgs.lib.licenses.mit ];
@@ -2020,11 +2061,11 b' self: super: {'
2020 };
2061 };
2021 };
2062 };
2022 "sqlalchemy" = super.buildPythonPackage {
2063 "sqlalchemy" = super.buildPythonPackage {
2023 name = "sqlalchemy-1.3.11";
2064 name = "sqlalchemy-1.3.15";
2024 doCheck = false;
2065 doCheck = false;
2025 src = fetchurl {
2066 src = fetchurl {
2026 url = "https://files.pythonhosted.org/packages/34/5c/0e1d7ad0ca52544bb12f9cb8d5cc454af45821c92160ffedd38db0a317f6/SQLAlchemy-1.3.11.tar.gz";
2067 url = "https://files.pythonhosted.org/packages/8c/30/4134e726dd5ed13728ff814fa91fc01c447ad8700504653fe99d91fdd34b/SQLAlchemy-1.3.15.tar.gz";
2027 sha256 = "12izpqqgy738ndn7qqn962qxi8qw2xb9vg2i880x12paklg599dg";
2068 sha256 = "0iglkvymfp35zm5pxy5kzqvcv96kkas0chqdx7xpla86sspa9k64";
2028 };
2069 };
2029 meta = {
2070 meta = {
2030 license = [ pkgs.lib.licenses.mit ];
2071 license = [ pkgs.lib.licenses.mit ];
@@ -2212,11 +2253,11 b' self: super: {'
2212 };
2253 };
2213 };
2254 };
2214 "wcwidth" = super.buildPythonPackage {
2255 "wcwidth" = super.buildPythonPackage {
2215 name = "wcwidth-0.1.7";
2256 name = "wcwidth-0.1.9";
2216 doCheck = false;
2257 doCheck = false;
2217 src = fetchurl {
2258 src = fetchurl {
2218 url = "https://files.pythonhosted.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
2259 url = "https://files.pythonhosted.org/packages/25/9d/0acbed6e4a4be4fc99148f275488580968f44ddb5e69b8ceb53fc9df55a0/wcwidth-0.1.9.tar.gz";
2219 sha256 = "0pn6dflzm609m4r3i8ik5ni9ijjbb5fa3vg1n7hn6vkd49r77wrx";
2260 sha256 = "1wf5ycjx8s066rdvr0fgz4xds9a8zhs91c4jzxvvymm1c8l8cwzf";
2220 };
2261 };
2221 meta = {
2262 meta = {
2222 license = [ pkgs.lib.licenses.mit ];
2263 license = [ pkgs.lib.licenses.mit ];
@@ -2234,7 +2275,7 b' self: super: {'
2234 };
2275 };
2235 };
2276 };
2236 "weberror" = super.buildPythonPackage {
2277 "weberror" = super.buildPythonPackage {
2237 name = "weberror-0.10.3";
2278 name = "weberror-0.13.1";
2238 doCheck = false;
2279 doCheck = false;
2239 propagatedBuildInputs = [
2280 propagatedBuildInputs = [
2240 self."webob"
2281 self."webob"
@@ -2243,8 +2284,8 b' self: super: {'
2243 self."paste"
2284 self."paste"
2244 ];
2285 ];
2245 src = fetchurl {
2286 src = fetchurl {
2246 url = "https://files.pythonhosted.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
2287 url = "https://files.pythonhosted.org/packages/07/0a/09ca5eb0fab5c0d17b380026babe81c96ecebb13f2b06c3203432dd7be72/WebError-0.13.1.tar.gz";
2247 sha256 = "0frg4kvycqpj5bi8asfqfs6bxsr2cvjvb6b56c4d1ai1z57kbjx6";
2288 sha256 = "0r4qvnf2r92gfnpa1kwygh4j2x6j3axg2i4an6hyxwg2gpaqp7y1";
2248 };
2289 };
2249 meta = {
2290 meta = {
2250 license = [ pkgs.lib.licenses.mit ];
2291 license = [ pkgs.lib.licenses.mit ];
@@ -2277,7 +2318,7 b' self: super: {'
2277 };
2318 };
2278 };
2319 };
2279 "webtest" = super.buildPythonPackage {
2320 "webtest" = super.buildPythonPackage {
2280 name = "webtest-2.0.33";
2321 name = "webtest-2.0.34";
2281 doCheck = false;
2322 doCheck = false;
2282 propagatedBuildInputs = [
2323 propagatedBuildInputs = [
2283 self."six"
2324 self."six"
@@ -2286,8 +2327,8 b' self: super: {'
2286 self."beautifulsoup4"
2327 self."beautifulsoup4"
2287 ];
2328 ];
2288 src = fetchurl {
2329 src = fetchurl {
2289 url = "https://files.pythonhosted.org/packages/a8/b0/ffc9413b637dbe26e291429bb0f6ed731e518d0cd03da28524a8fe2e8a8f/WebTest-2.0.33.tar.gz";
2330 url = "https://files.pythonhosted.org/packages/2c/74/a0e63feee438735d628631e2b70d82280276a930637ac535479e5fad9427/WebTest-2.0.34.tar.gz";
2290 sha256 = "1l3z0cwqslsf4rcrhi2gr8kdfh74wn2dw76376i4g9i38gz8wd21";
2331 sha256 = "0x1y2c8z4fmpsny4hbp6ka37si2g10r5r2jwxhvv5mx7g3blq4bi";
2291 };
2332 };
2292 meta = {
2333 meta = {
2293 license = [ pkgs.lib.licenses.mit ];
2334 license = [ pkgs.lib.licenses.mit ];
@@ -2327,14 +2368,14 b' self: super: {'
2327 };
2368 };
2328 };
2369 };
2329 "zipp" = super.buildPythonPackage {
2370 "zipp" = super.buildPythonPackage {
2330 name = "zipp-0.6.0";
2371 name = "zipp-1.2.0";
2331 doCheck = false;
2372 doCheck = false;
2332 propagatedBuildInputs = [
2373 propagatedBuildInputs = [
2333 self."more-itertools"
2374 self."contextlib2"
2334 ];
2375 ];
2335 src = fetchurl {
2376 src = fetchurl {
2336 url = "https://files.pythonhosted.org/packages/57/dd/585d728479d97d25aeeb9aa470d36a4ad8d0ba5610f84e14770128ce6ff7/zipp-0.6.0.tar.gz";
2377 url = "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz";
2337 sha256 = "13ndkf7vklw978a4gdl1yfvn8hch28429a0iam67sg4nrp5v261p";
2378 sha256 = "1c91lnv1bxjimh8as27hz7bghsjkkbxn1d37xq7in9c82iai0167";
2338 };
2379 };
2339 meta = {
2380 meta = {
2340 license = [ pkgs.lib.licenses.mit ];
2381 license = [ pkgs.lib.licenses.mit ];
@@ -3,7 +3,7 b''
3 amqp==2.5.2
3 amqp==2.5.2
4 babel==1.3
4 babel==1.3
5 beaker==1.9.1
5 beaker==1.9.1
6 bleach==3.1.0
6 bleach==3.1.3
7 celery==4.3.0
7 celery==4.3.0
8 channelstream==0.5.2
8 channelstream==0.5.2
9 click==7.0
9 click==7.0
@@ -14,7 +14,7 b' cssselect==1.0.3'
14 cryptography==2.6.1
14 cryptography==2.6.1
15 decorator==4.1.2
15 decorator==4.1.2
16 deform==2.0.8
16 deform==2.0.8
17 docutils==0.14.0
17 docutils==0.16.0
18 dogpile.cache==0.9.0
18 dogpile.cache==0.9.0
19 dogpile.core==0.4.1
19 dogpile.core==0.4.1
20 formencode==1.2.4
20 formencode==1.2.4
@@ -30,38 +30,39 b' markdown==2.6.11'
30 markupsafe==1.1.1
30 markupsafe==1.1.1
31 msgpack-python==0.5.6
31 msgpack-python==0.5.6
32 pyotp==2.3.0
32 pyotp==2.3.0
33 packaging==19.2
33 packaging==20.3
34 pathlib2==2.3.5
34 pathlib2==2.3.5
35 paste==3.2.1
35 paste==3.4.0
36 pastedeploy==2.0.1
36 pastedeploy==2.1.0
37 pastescript==3.2.0
37 pastescript==3.2.0
38 peppercorn==0.6
38 peppercorn==0.6
39 psutil==5.6.5
39 premailer==3.6.1
40 psutil==5.7.0
40 py-bcrypt==0.4
41 py-bcrypt==0.4
41 pycurl==7.43.0.3
42 pycurl==7.43.0.3
42 pycrypto==2.6.1
43 pycrypto==2.6.1
43 pygments==2.4.2
44 pygments==2.4.2
44 pyparsing==2.4.5
45 pyparsing==2.4.7
45 pyramid-debugtoolbar==4.5.1
46 pyramid-debugtoolbar==4.6.1
46 pyramid-mako==1.1.0
47 pyramid-mako==1.1.0
47 pyramid==1.10.4
48 pyramid==1.10.4
48 pyramid_mailer==0.15.1
49 pyramid_mailer==0.15.1
49 python-dateutil==2.8.1
50 python-dateutil==2.8.1
50 python-ldap==3.1.0
51 python-ldap==3.2.0
51 python-memcached==1.59
52 python-memcached==1.59
52 python-pam==1.8.4
53 python-pam==1.8.4
53 python-saml==2.4.2
54 python-saml==2.4.2
54 pytz==2019.2
55 pytz==2019.3
55 tzlocal==1.5.1
56 tzlocal==1.5.1
56 pyzmq==14.6.0
57 pyzmq==14.6.0
57 py-gfm==0.1.4
58 py-gfm==0.1.4
58 redis==3.3.11
59 redis==3.4.1
59 repoze.lru==0.7
60 repoze.lru==0.7
60 requests==2.22.0
61 requests==2.22.0
61 routes==2.4.1
62 routes==2.4.1
62 simplejson==3.16.0
63 simplejson==3.16.0
63 six==1.11.0
64 six==1.11.0
64 sqlalchemy==1.3.11
65 sqlalchemy==1.3.15
65 sshpubkeys==3.1.0
66 sshpubkeys==3.1.0
66 subprocess32==3.5.4
67 subprocess32==3.5.4
67 supervisor==4.1.0
68 supervisor==4.1.0
@@ -69,7 +70,7 b' translationstring==1.3'
69 urllib3==1.25.2
70 urllib3==1.25.2
70 urlobject==2.4.3
71 urlobject==2.4.3
71 venusian==1.2.0
72 venusian==1.2.0
72 weberror==0.10.3
73 weberror==0.13.1
73 webhelpers2==2.0
74 webhelpers2==2.0
74 webob==1.8.5
75 webob==1.8.5
75 whoosh==2.7.4
76 whoosh==2.7.4
@@ -94,18 +95,18 b' jupyter-client==5.0.0'
94 jupyter-core==4.5.0
95 jupyter-core==4.5.0
95
96
96 ## cli tools
97 ## cli tools
97 alembic==1.3.1
98 alembic==1.4.2
98 invoke==0.13.0
99 invoke==0.13.0
99 bumpversion==0.5.3
100 bumpversion==0.5.3
100
101
101 ## http servers
102 ## http servers
102 gevent==1.4.0
103 gevent==1.5.0
103 greenlet==0.4.15
104 greenlet==0.4.15
104 gunicorn==19.9.0
105 gunicorn==19.9.0
105 waitress==1.3.1
106 waitress==1.3.1
106
107
107 ## debug
108 ## debug
108 ipdb==0.12.0
109 ipdb==0.13.2
109 ipython==5.1.0
110 ipython==5.1.0
110
111
111 ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version
112 ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version
@@ -10,9 +10,9 b' configparser==4.0.2'
10 contextlib2==0.6.0.post1
10 contextlib2==0.6.0.post1
11 ecdsa==0.13.2
11 ecdsa==0.13.2
12 gnureadline==6.3.8
12 gnureadline==6.3.8
13 hupper==1.9.1
13 hupper==1.10.2
14 ipaddress==1.0.23
14 ipaddress==1.0.23
15 importlib-metadata==0.23
15 importlib-metadata==1.6.0
16 jinja2==2.9.6
16 jinja2==2.9.6
17 jsonschema==2.6.0
17 jsonschema==2.6.0
18 pluggy==0.13.1
18 pluggy==0.13.1
@@ -24,4 +24,4 b' tempita==0.5.2'
24 testpath==0.4.4
24 testpath==0.4.4
25 transaction==2.4.0
25 transaction==2.4.0
26 vine==1.3.0
26 vine==1.3.0
27 wcwidth==0.1.7
27 wcwidth==0.1.9
@@ -12,5 +12,5 b' mock==3.0.5'
12 cov-core==1.15.0
12 cov-core==1.15.0
13 coverage==4.5.4
13 coverage==4.5.4
14
14
15 webtest==2.0.33
15 webtest==2.0.34
16 beautifulsoup4==4.6.3
16 beautifulsoup4==4.6.3
@@ -1,1 +1,1 b''
1 4.18.3 No newline at end of file
1 4.19.0 No newline at end of file
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,17 +19,20 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 from collections import OrderedDict
23
22 import sys
24 import sys
23 import platform
25 import platform
24
26
25 VERSION = tuple(open(os.path.join(
27 VERSION = tuple(open(os.path.join(
26 os.path.dirname(__file__), 'VERSION')).read().split('.'))
28 os.path.dirname(__file__), 'VERSION')).read().split('.'))
27
29
28 BACKENDS = {
30 BACKENDS = OrderedDict()
29 'hg': 'Mercurial repository',
31
30 'git': 'Git repository',
32 BACKENDS['hg'] = 'Mercurial repository'
31 'svn': 'Subversion repository',
33 BACKENDS['git'] = 'Git repository'
32 }
34 BACKENDS['svn'] = 'Subversion repository'
35
33
36
34 CELERY_ENABLED = False
37 CELERY_ENABLED = False
35 CELERY_EAGER = False
38 CELERY_EAGER = False
@@ -45,7 +48,7 b' PYRAMID_SETTINGS = {}'
45 EXTENSIONS = {}
48 EXTENSIONS = {}
46
49
47 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
50 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
48 __dbversion__ = 103 # defines current db version for migrations
51 __dbversion__ = 107 # defines current db version for migrations
49 __platform__ = platform.system()
52 __platform__ = platform.system()
50 __license__ = 'AGPLv3, and Commercial License'
53 __license__ = 'AGPLv3, and Commercial License'
51 __author__ = 'RhodeCode GmbH'
54 __author__ = 'RhodeCode GmbH'
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -60,7 +60,10 b' class TestGetPullRequestComments(object)'
60 'comment_text': 'Auto status change to |new_status|\n\n.. |new_status| replace:: *"Under Review"*',
60 'comment_text': 'Auto status change to |new_status|\n\n.. |new_status| replace:: *"Under Review"*',
61 'comment_type': 'note',
61 'comment_type': 'note',
62 'comment_resolved_by': None,
62 'comment_resolved_by': None,
63 'pull_request_version': None}
63 'pull_request_version': None,
64 'comment_commit_id': None,
65 'comment_pull_request_id': pull_request.pull_request_id
66 }
64 ]
67 ]
65 assert_ok(id_, expected, response.body)
68 assert_ok(id_, expected, response.body)
66
69
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,7 +49,8 b' def assert_call_ok(id_, given):'
49 def assert_ok(id_, expected, given):
49 def assert_ok(id_, expected, given):
50 given = json.loads(given)
50 given = json.loads(given)
51 if given.get('error'):
51 if given.get('error'):
52 pytest.fail("Unexpected ERROR in success response: {}".format(given['error']))
52 err = given['error']
53 pytest.fail(u"Unexpected ERROR in success response: {}".format(err))
53
54
54 expected = jsonify({
55 expected = jsonify({
55 'id': id_,
56 'id': id_,
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2019 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -379,7 +379,9 b' def get_pull_request_comments('
379 },
379 },
380 "comment_text": "Example text",
380 "comment_text": "Example text",
381 "comment_type": null,
381 "comment_type": null,
382 "pull_request_version": null
382 "pull_request_version": null,
383 "comment_commit_id": None,
384 "comment_pull_request_id": <pull_request_id>
383 }
385 }
384 ],
386 ],
385 error : null
387 error : null
@@ -684,28 +686,9 b' def create_pull_request('
684 full_source_ref = resolve_ref_or_error(source_ref, source_db_repo)
686 full_source_ref = resolve_ref_or_error(source_ref, source_db_repo)
685 full_target_ref = resolve_ref_or_error(target_ref, target_db_repo)
687 full_target_ref = resolve_ref_or_error(target_ref, target_db_repo)
686
688
687 source_scm = source_db_repo.scm_instance()
688 target_scm = target_db_repo.scm_instance()
689
690 source_commit = get_commit_or_error(full_source_ref, source_db_repo)
689 source_commit = get_commit_or_error(full_source_ref, source_db_repo)
691 target_commit = get_commit_or_error(full_target_ref, target_db_repo)
690 target_commit = get_commit_or_error(full_target_ref, target_db_repo)
692
691
693 ancestor = source_scm.get_common_ancestor(
694 source_commit.raw_id, target_commit.raw_id, target_scm)
695 if not ancestor:
696 raise JSONRPCError('no common ancestor found')
697
698 # recalculate target ref based on ancestor
699 target_ref_type, target_ref_name, __ = full_target_ref.split(':')
700 full_target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
701
702 commit_ranges = target_scm.compare(
703 target_commit.raw_id, source_commit.raw_id, source_scm,
704 merge=True, pre_load=[])
705
706 if not commit_ranges:
707 raise JSONRPCError('no commits found')
708
709 reviewer_objects = Optional.extract(reviewers) or []
692 reviewer_objects = Optional.extract(reviewers) or []
710
693
711 # serialize and validate passed in given reviewers
694 # serialize and validate passed in given reviewers
@@ -725,16 +708,16 b' def create_pull_request('
725 PullRequestModel().get_reviewer_functions()
708 PullRequestModel().get_reviewer_functions()
726
709
727 # recalculate reviewers logic, to make sure we can validate this
710 # recalculate reviewers logic, to make sure we can validate this
728 reviewer_rules = get_default_reviewers_data(
711 default_reviewers_data = get_default_reviewers_data(
729 owner, source_db_repo,
712 owner, source_db_repo,
730 source_commit, target_db_repo, target_commit)
713 source_commit, target_db_repo, target_commit)
731
714
732 # now MERGE our given with the calculated
715 # now MERGE our given with the calculated
733 reviewer_objects = reviewer_rules['reviewers'] + reviewer_objects
716 reviewer_objects = default_reviewers_data['reviewers'] + reviewer_objects
734
717
735 try:
718 try:
736 reviewers = validate_default_reviewers(
719 reviewers = validate_default_reviewers(
737 reviewer_objects, reviewer_rules)
720 reviewer_objects, default_reviewers_data)
738 except ValueError as e:
721 except ValueError as e:
739 raise JSONRPCError('Reviewers Validation: {}'.format(e))
722 raise JSONRPCError('Reviewers Validation: {}'.format(e))
740
723
@@ -746,6 +729,24 b' def create_pull_request('
746 source_ref=title_source_ref,
729 source_ref=title_source_ref,
747 target=target_repo
730 target=target_repo
748 )
731 )
732
733 diff_info = default_reviewers_data['diff_info']
734 common_ancestor_id = diff_info['ancestor']
735 commits = diff_info['commits']
736
737 if not common_ancestor_id:
738 raise JSONRPCError('no common ancestor found')
739
740 if not commits:
741 raise JSONRPCError('no commits found')
742
743 # NOTE(marcink): reversed is consistent with how we open it in the WEB interface
744 revisions = [commit.raw_id for commit in reversed(commits)]
745
746 # recalculate target ref based on ancestor
747 target_ref_type, target_ref_name, __ = full_target_ref.split(':')
748 full_target_ref = ':'.join((target_ref_type, target_ref_name, common_ancestor_id))
749
749 # fetch renderer, if set fallback to plain in case of PR
750 # fetch renderer, if set fallback to plain in case of PR
750 rc_config = SettingsModel().get_all_settings()
751 rc_config = SettingsModel().get_all_settings()
751 default_system_renderer = rc_config.get('rhodecode_markup_renderer', 'plain')
752 default_system_renderer = rc_config.get('rhodecode_markup_renderer', 'plain')
@@ -758,12 +759,13 b' def create_pull_request('
758 source_ref=full_source_ref,
759 source_ref=full_source_ref,
759 target_repo=target_repo,
760 target_repo=target_repo,
760 target_ref=full_target_ref,
761 target_ref=full_target_ref,
761 revisions=[commit.raw_id for commit in reversed(commit_ranges)],
762 common_ancestor_id=common_ancestor_id,
763 revisions=revisions,
762 reviewers=reviewers,
764 reviewers=reviewers,
763 title=title,
765 title=title,
764 description=description,
766 description=description,
765 description_renderer=description_renderer,
767 description_renderer=description_renderer,
766 reviewer_data=reviewer_rules,
768 reviewer_data=default_reviewers_data,
767 auth_user=apiuser
769 auth_user=apiuser
768 )
770 )
769
771
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -33,7 +33,7 b' from rhodecode.lib import audit_logger, '
33 from rhodecode.lib import repo_maintenance
33 from rhodecode.lib import repo_maintenance
34 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
34 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
35 from rhodecode.lib.celerylib.utils import get_task_id
35 from rhodecode.lib.celerylib.utils import get_task_id
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int, safe_unicode
37 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
38 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
39 from rhodecode.lib.vcs import RepositoryError
39 from rhodecode.lib.vcs import RepositoryError
@@ -573,6 +573,7 b' def get_repo_file(request, apiuser, repo'
573 elif details == 'full':
573 elif details == 'full':
574 extended_info = content = True
574 extended_info = content = True
575
575
576 file_path = safe_unicode(file_path)
576 try:
577 try:
577 # check if repo is not empty by any chance, skip quicker if it is.
578 # check if repo is not empty by any chance, skip quicker if it is.
578 _scm = repo.scm_instance()
579 _scm = repo.scm_instance()
@@ -583,12 +584,12 b' def get_repo_file(request, apiuser, repo'
583 repo, commit_id, file_path, extended_info=extended_info,
584 repo, commit_id, file_path, extended_info=extended_info,
584 content=content, max_file_bytes=max_file_bytes, cache=cache)
585 content=content, max_file_bytes=max_file_bytes, cache=cache)
585 except NodeDoesNotExistError:
586 except NodeDoesNotExistError:
586 raise JSONRPCError('There is no file in repo: `{}` at path `{}` for commit: `{}`'.format(
587 raise JSONRPCError(u'There is no file in repo: `{}` at path `{}` for commit: `{}`'.format(
587 repo.repo_name, file_path, commit_id))
588 repo.repo_name, file_path, commit_id))
588 except Exception:
589 except Exception:
589 log.exception("Exception occurred while trying to get repo %s file",
590 log.exception(u"Exception occurred while trying to get repo %s file",
590 repo.repo_name)
591 repo.repo_name)
591 raise JSONRPCError('failed to get repo: `{}` file at path {}'.format(
592 raise JSONRPCError(u'failed to get repo: `{}` file at path {}'.format(
592 repo.repo_name, file_path))
593 repo.repo_name, file_path))
593
594
594 return node
595 return node
@@ -1599,7 +1600,8 b' def comment_commit('
1599 validate_repo_permissions(apiuser, repoid, repo, _perms)
1600 validate_repo_permissions(apiuser, repoid, repo, _perms)
1600
1601
1601 try:
1602 try:
1602 commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id
1603 commit = repo.scm_instance().get_commit(commit_id=commit_id)
1604 commit_id = commit.raw_id
1603 except Exception as e:
1605 except Exception as e:
1604 log.exception('Failed to fetch commit')
1606 log.exception('Failed to fetch commit')
1605 raise JSONRPCError(safe_str(e))
1607 raise JSONRPCError(safe_str(e))
@@ -1655,10 +1657,14 b' def comment_commit('
1655 except StatusChangeOnClosedPullRequestError:
1657 except StatusChangeOnClosedPullRequestError:
1656 log.exception(
1658 log.exception(
1657 "Exception occurred while trying to change repo commit status")
1659 "Exception occurred while trying to change repo commit status")
1658 msg = ('Changing status on a changeset associated with '
1660 msg = ('Changing status on a commit associated with '
1659 'a closed pull request is not allowed')
1661 'a closed pull request is not allowed')
1660 raise JSONRPCError(msg)
1662 raise JSONRPCError(msg)
1661
1663
1664 CommentsModel().trigger_commit_comment_hook(
1665 repo, apiuser, 'create',
1666 data={'comment': comment, 'commit': commit})
1667
1662 Session().commit()
1668 Session().commit()
1663 return {
1669 return {
1664 'msg': (
1670 'msg': (
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,10 +22,12 b''
22 import logging
22 import logging
23
23
24 from rhodecode.api import jsonrpc_method
24 from rhodecode.api import jsonrpc_method
25 from rhodecode.api.exc import JSONRPCValidationError
25 from rhodecode.api.exc import JSONRPCValidationError, JSONRPCForbidden
26 from rhodecode.api.utils import Optional
26 from rhodecode.api.utils import Optional, has_superadmin_permission
27 from rhodecode.lib.index import searcher_from_config
27 from rhodecode.lib.index import searcher_from_config
28 from rhodecode.lib.user_log_filter import user_log_filter
28 from rhodecode.model import validation_schema
29 from rhodecode.model import validation_schema
30 from rhodecode.model.db import joinedload, UserLog
29 from rhodecode.model.validation_schema.schemas import search_schema
31 from rhodecode.model.validation_schema.schemas import search_schema
30
32
31 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
@@ -116,3 +118,35 b' def search(request, apiuser, search_quer'
116 colander_exc=validation_schema.Invalid(node, search_result['error']))
118 colander_exc=validation_schema.Invalid(node, search_result['error']))
117
119
118 return data
120 return data
121
122
123 @jsonrpc_method()
124 def get_audit_logs(request, apiuser, query):
125 """
126 return full audit logs based on the query.
127
128 Please see `example query in admin > settings > audit logs` for examples
129
130 :param apiuser: This is filled automatically from the |authtoken|.
131 :type apiuser: AuthUser
132 :param query: filter query, example: action:repo.artifact.add date:[20200401 TO 20200601]"
133 :type query: str
134 """
135
136 if not has_superadmin_permission(apiuser):
137 raise JSONRPCForbidden()
138
139 filter_term = query
140 ret = []
141
142 # show all user actions
143 user_log = UserLog.query() \
144 .options(joinedload(UserLog.user)) \
145 .options(joinedload(UserLog.repository)) \
146 .order_by(UserLog.action_date.desc())
147
148 audit_log = user_log_filter(user_log, filter_term)
149
150 for entry in audit_log:
151 ret.append(entry)
152 return ret
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -396,7 +396,11 b' class PathFilter(object):'
396 def path_access_allowed(self, path):
396 def path_access_allowed(self, path):
397 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
397 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
398 if self.permission_checker:
398 if self.permission_checker:
399 return path and self.permission_checker.has_access(path)
399 has_access = path and self.permission_checker.has_access(path)
400 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
401 return has_access
402
403 log.debug('ACL permissions checker not enabled, skipping...')
400 return True
404 return True
401
405
402 def filter_patchset(self, patchset):
406 def filter_patchset(self, patchset):
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,6 +25,15 b' from rhodecode.lib import rc_cache'
25
25
26 log = logging.getLogger(__name__)
26 log = logging.getLogger(__name__)
27
27
28 # names of namespaces used for different permission related cached
29 # during flush operation we need to take care of all those
30 cache_namespaces = [
31 'cache_user_auth.{}',
32 'cache_user_repo_acl_ids.{}',
33 'cache_user_user_group_acl_ids.{}',
34 'cache_user_repo_group_acl_ids.{}'
35 ]
36
28
37
29 def trigger_user_permission_flush(event):
38 def trigger_user_permission_flush(event):
30 """
39 """
@@ -35,9 +44,11 b' def trigger_user_permission_flush(event)'
35
44
36 affected_user_ids = set(event.user_ids)
45 affected_user_ids = set(event.user_ids)
37 for user_id in affected_user_ids:
46 for user_id in affected_user_ids:
38 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
47 for cache_namespace_uid_tmpl in cache_namespaces:
39 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
48 cache_namespace_uid = cache_namespace_uid_tmpl.format(user_id)
40 log.debug('Deleted %s cache keys for user_id: %s', del_keys, user_id)
49 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
50 log.debug('Deleted %s cache keys for user_id: %s and namespace %s',
51 del_keys, user_id, cache_namespace_uid)
41
52
42
53
43 def includeme(config):
54 def includeme(config):
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +26,6 b' def admin_routes(config):'
26 """
26 """
27 Admin prefixed routes
27 Admin prefixed routes
28 """
28 """
29
30 config.add_route(
29 config.add_route(
31 name='admin_audit_logs',
30 name='admin_audit_logs',
32 pattern='/audit_logs')
31 pattern='/audit_logs')
@@ -291,12 +290,22 b' def admin_routes(config):'
291 pattern='/users/{user_id:\d+}/create_repo_group',
290 pattern='/users/{user_id:\d+}/create_repo_group',
292 user_route=True)
291 user_route=True)
293
292
293 # user notice
294 config.add_route(
295 name='user_notice_dismiss',
296 pattern='/users/{user_id:\d+}/notice_dismiss',
297 user_route=True)
298
294 # user auth tokens
299 # user auth tokens
295 config.add_route(
300 config.add_route(
296 name='edit_user_auth_tokens',
301 name='edit_user_auth_tokens',
297 pattern='/users/{user_id:\d+}/edit/auth_tokens',
302 pattern='/users/{user_id:\d+}/edit/auth_tokens',
298 user_route=True)
303 user_route=True)
299 config.add_route(
304 config.add_route(
305 name='edit_user_auth_tokens_view',
306 pattern='/users/{user_id:\d+}/edit/auth_tokens/view',
307 user_route=True)
308 config.add_route(
300 name='edit_user_auth_tokens_add',
309 name='edit_user_auth_tokens_add',
301 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
310 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
302 user_route=True)
311 user_route=True)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -228,7 +228,7 b' class TestAdminPermissionsController(Tes'
228 self.log_user()
228 self.log_user()
229
229
230 # ADD
230 # ADD
231 default_user_id = User.get_default_user().user_id
231 default_user_id = User.get_default_user_id()
232 self.app.post(
232 self.app.post(
233 route_path('edit_user_ips_add', user_id=default_user_id),
233 route_path('edit_user_ips_add', user_id=default_user_id),
234 params={'new_ip': '0.0.0.0/24', 'csrf_token': self.csrf_token})
234 params={'new_ip': '0.0.0.0/24', 'csrf_token': self.csrf_token})
@@ -238,7 +238,7 b' class TestAdminPermissionsController(Tes'
238 response.mustcontain('0.0.0.0 - 0.0.0.255')
238 response.mustcontain('0.0.0.0 - 0.0.0.255')
239
239
240 # DELETE
240 # DELETE
241 default_user_id = User.get_default_user().user_id
241 default_user_id = User.get_default_user_id()
242 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
242 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
243 default_user_id).first().ip_id
243 default_user_id).first().ip_id
244
244
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -84,15 +84,13 b' class TestAdminRepos(object):'
84 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
84 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
85 response = self.app.get(route_path('repo_new'), status=200)
85 response = self.app.get(route_path('repo_new'), status=200)
86 assert_response = response.assert_response()
86 assert_response = response.assert_response()
87 element = assert_response.get_element('#repo_type')
87 element = assert_response.get_element('[name=repo_type]')
88 assert element.text_content() == '\ngit\n'
88 assert element.get('value') == 'git'
89
89
90 def test_create_page_non_restricted_backends(self, autologin_user, backend):
90 def test_create_page_non_restricted_backends(self, autologin_user, backend):
91 response = self.app.get(route_path('repo_new'), status=200)
91 response = self.app.get(route_path('repo_new'), status=200)
92 assert_response = response.assert_response()
92 assert_response = response.assert_response()
93 assert_response.element_contains('#repo_type', 'git')
93 assert ['hg', 'git', 'svn'] == [x.get('value') for x in assert_response.get_elements('[name=repo_type]')]
94 assert_response.element_contains('#repo_type', 'svn')
95 assert_response.element_contains('#repo_type', 'hg')
96
94
97 @pytest.mark.parametrize(
95 @pytest.mark.parametrize(
98 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
96 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -143,7 +143,7 b' class TestAdminUsersView(TestController)'
143 response = self.app.get(
143 response = self.app.get(
144 route_path('edit_user_auth_tokens', user_id=user_id))
144 route_path('edit_user_auth_tokens', user_id=user_id))
145 for token in auth_tokens:
145 for token in auth_tokens:
146 response.mustcontain(token)
146 response.mustcontain(token[:4])
147 response.mustcontain('never')
147 response.mustcontain('never')
148
148
149 @pytest.mark.parametrize("desc, lifetime", [
149 @pytest.mark.parametrize("desc, lifetime", [
@@ -165,7 +165,7 b' class TestAdminUsersView(TestController)'
165 response = response.follow()
165 response = response.follow()
166 user = User.get(user_id)
166 user = User.get(user_id)
167 for auth_token in user.auth_tokens:
167 for auth_token in user.auth_tokens:
168 response.mustcontain(auth_token)
168 response.mustcontain(auth_token[:4])
169
169
170 def test_delete_auth_token(self, user_util):
170 def test_delete_auth_token(self, user_util):
171 self.log_user()
171 self.log_user()
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2018-2019 RhodeCode GmbH
3 # Copyright (C) 2018-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -142,7 +142,7 b' class AdminPermissionsView(BaseAppView, '
142 h.flash(_('Error occurred during update of permissions'),
142 h.flash(_('Error occurred during update of permissions'),
143 category='error')
143 category='error')
144
144
145 affected_user_ids = [User.get_default_user().user_id]
145 affected_user_ids = [User.get_default_user_id()]
146 PermissionModel().trigger_permission_flush(affected_user_ids)
146 PermissionModel().trigger_permission_flush(affected_user_ids)
147
147
148 raise HTTPFound(h.route_path('admin_permissions_application'))
148 raise HTTPFound(h.route_path('admin_permissions_application'))
@@ -218,7 +218,7 b' class AdminPermissionsView(BaseAppView, '
218 h.flash(_('Error occurred during update of permissions'),
218 h.flash(_('Error occurred during update of permissions'),
219 category='error')
219 category='error')
220
220
221 affected_user_ids = [User.get_default_user().user_id]
221 affected_user_ids = [User.get_default_user_id()]
222 PermissionModel().trigger_permission_flush(affected_user_ids)
222 PermissionModel().trigger_permission_flush(affected_user_ids)
223
223
224 raise HTTPFound(h.route_path('admin_permissions_object'))
224 raise HTTPFound(h.route_path('admin_permissions_object'))
@@ -320,7 +320,7 b' class AdminPermissionsView(BaseAppView, '
320 h.flash(_('Error occurred during update of permissions'),
320 h.flash(_('Error occurred during update of permissions'),
321 category='error')
321 category='error')
322
322
323 affected_user_ids = [User.get_default_user().user_id]
323 affected_user_ids = [User.get_default_user_id()]
324 PermissionModel().trigger_permission_flush(affected_user_ids)
324 PermissionModel().trigger_permission_flush(affected_user_ids)
325
325
326 raise HTTPFound(h.route_path('admin_permissions_global'))
326 raise HTTPFound(h.route_path('admin_permissions_global'))
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -41,9 +41,9 b' class SvnConfigAdminSettingsView(BaseApp'
41 def vcs_svn_generate_config(self):
41 def vcs_svn_generate_config(self):
42 _ = self.request.translate
42 _ = self.request.translate
43 try:
43 try:
44 generate_mod_dav_svn_config(self.request.registry)
44 file_path = generate_mod_dav_svn_config(self.request.registry)
45 msg = {
45 msg = {
46 'message': _('Apache configuration for Subversion generated.'),
46 'message': _('Apache configuration for Subversion generated at `{}`.').format(file_path),
47 'level': 'success',
47 'level': 'success',
48 }
48 }
49 except Exception:
49 except Exception:
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -34,12 +34,13 b' from rhodecode.apps.ssh_support import S'
34 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
34 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
35 from rhodecode.authentication.plugins import auth_rhodecode
35 from rhodecode.authentication.plugins import auth_rhodecode
36 from rhodecode.events import trigger
36 from rhodecode.events import trigger
37 from rhodecode.model.db import true
37 from rhodecode.model.db import true, UserNotice
38
38
39 from rhodecode.lib import audit_logger, rc_cache
39 from rhodecode.lib import audit_logger, rc_cache
40 from rhodecode.lib.exceptions import (
40 from rhodecode.lib.exceptions import (
41 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
42 UserOwnsUserGroupsException, DefaultUserException)
42 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
43 UserOwnsArtifactsException, DefaultUserException)
43 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.auth import (
45 from rhodecode.lib.auth import (
45 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
46 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
@@ -377,11 +378,13 b' class UsersView(UserAppView):'
377 _repos = c.user.repositories
378 _repos = c.user.repositories
378 _repo_groups = c.user.repository_groups
379 _repo_groups = c.user.repository_groups
379 _user_groups = c.user.user_groups
380 _user_groups = c.user.user_groups
381 _pull_requests = c.user.user_pull_requests
380 _artifacts = c.user.artifacts
382 _artifacts = c.user.artifacts
381
383
382 handle_repos = None
384 handle_repos = None
383 handle_repo_groups = None
385 handle_repo_groups = None
384 handle_user_groups = None
386 handle_user_groups = None
387 handle_pull_requests = None
385 handle_artifacts = None
388 handle_artifacts = None
386
389
387 # calls for flash of handle based on handle case detach or delete
390 # calls for flash of handle based on handle case detach or delete
@@ -412,6 +415,15 b' class UsersView(UserAppView):'
412 h.flash(_('Deleted %s user groups') % len(_user_groups),
415 h.flash(_('Deleted %s user groups') % len(_user_groups),
413 category='success')
416 category='success')
414
417
418 def set_handle_flash_pull_requests():
419 handle = handle_pull_requests
420 if handle == 'detach':
421 h.flash(_('Detached %s pull requests') % len(_pull_requests),
422 category='success')
423 elif handle == 'delete':
424 h.flash(_('Deleted %s pull requests') % len(_pull_requests),
425 category='success')
426
415 def set_handle_flash_artifacts():
427 def set_handle_flash_artifacts():
416 handle = handle_artifacts
428 handle = handle_artifacts
417 if handle == 'detach':
429 if handle == 'detach':
@@ -421,6 +433,12 b' class UsersView(UserAppView):'
421 h.flash(_('Deleted %s artifacts') % len(_artifacts),
433 h.flash(_('Deleted %s artifacts') % len(_artifacts),
422 category='success')
434 category='success')
423
435
436 handle_user = User.get_first_super_admin()
437 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
438 if handle_user_id:
439 # NOTE(marcink): we get new owner for objects...
440 handle_user = User.get_or_404(handle_user_id)
441
424 if _repos and self.request.POST.get('user_repos'):
442 if _repos and self.request.POST.get('user_repos'):
425 handle_repos = self.request.POST['user_repos']
443 handle_repos = self.request.POST['user_repos']
426
444
@@ -430,16 +448,25 b' class UsersView(UserAppView):'
430 if _user_groups and self.request.POST.get('user_user_groups'):
448 if _user_groups and self.request.POST.get('user_user_groups'):
431 handle_user_groups = self.request.POST['user_user_groups']
449 handle_user_groups = self.request.POST['user_user_groups']
432
450
451 if _pull_requests and self.request.POST.get('user_pull_requests'):
452 handle_pull_requests = self.request.POST['user_pull_requests']
453
433 if _artifacts and self.request.POST.get('user_artifacts'):
454 if _artifacts and self.request.POST.get('user_artifacts'):
434 handle_artifacts = self.request.POST['user_artifacts']
455 handle_artifacts = self.request.POST['user_artifacts']
435
456
436 old_values = c.user.get_api_data()
457 old_values = c.user.get_api_data()
437
458
438 try:
459 try:
439 UserModel().delete(c.user, handle_repos=handle_repos,
460
440 handle_repo_groups=handle_repo_groups,
461 UserModel().delete(
441 handle_user_groups=handle_user_groups,
462 c.user,
442 handle_artifacts=handle_artifacts)
463 handle_repos=handle_repos,
464 handle_repo_groups=handle_repo_groups,
465 handle_user_groups=handle_user_groups,
466 handle_pull_requests=handle_pull_requests,
467 handle_artifacts=handle_artifacts,
468 handle_new_owner=handle_user
469 )
443
470
444 audit_logger.store_web(
471 audit_logger.store_web(
445 'user.delete', action_data={'old_data': old_values},
472 'user.delete', action_data={'old_data': old_values},
@@ -449,11 +476,13 b' class UsersView(UserAppView):'
449 set_handle_flash_repos()
476 set_handle_flash_repos()
450 set_handle_flash_repo_groups()
477 set_handle_flash_repo_groups()
451 set_handle_flash_user_groups()
478 set_handle_flash_user_groups()
479 set_handle_flash_pull_requests()
452 set_handle_flash_artifacts()
480 set_handle_flash_artifacts()
453 username = h.escape(old_values['username'])
481 username = h.escape(old_values['username'])
454 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
482 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
455 except (UserOwnsReposException, UserOwnsRepoGroupsException,
483 except (UserOwnsReposException, UserOwnsRepoGroupsException,
456 UserOwnsUserGroupsException, DefaultUserException) as e:
484 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
485 UserOwnsArtifactsException, DefaultUserException) as e:
457 h.flash(e, category='warning')
486 h.flash(e, category='warning')
458 except Exception:
487 except Exception:
459 log.exception("Exception during deletion of user")
488 log.exception("Exception during deletion of user")
@@ -502,6 +531,11 b' class UsersView(UserAppView):'
502 user_id = self.db_user_id
531 user_id = self.db_user_id
503 c.user = self.db_user
532 c.user = self.db_user
504
533
534 c.detach_user = User.get_first_super_admin()
535 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
536 if detach_user_id:
537 c.detach_user = User.get_or_404(detach_user_id)
538
505 c.active = 'advanced'
539 c.active = 'advanced'
506 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
540 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
507 c.personal_repo_group_name = RepoGroupModel()\
541 c.personal_repo_group_name = RepoGroupModel()\
@@ -511,7 +545,6 b' class UsersView(UserAppView):'
511 (x.user for x in c.user.user_review_rules),
545 (x.user for x in c.user.user_review_rules),
512 key=lambda u: u.username.lower())
546 key=lambda u: u.username.lower())
513
547
514 c.first_admin = User.get_first_super_admin()
515 defaults = c.user.get_dict()
548 defaults = c.user.get_dict()
516
549
517 # Interim workaround if the user participated on any pull requests as a
550 # Interim workaround if the user participated on any pull requests as a
@@ -705,6 +738,32 b' class UsersView(UserAppView):'
705 @HasPermissionAllDecorator('hg.admin')
738 @HasPermissionAllDecorator('hg.admin')
706 @CSRFRequired()
739 @CSRFRequired()
707 @view_config(
740 @view_config(
741 route_name='user_notice_dismiss', request_method='POST',
742 renderer='json_ext', xhr=True)
743 def user_notice_dismiss(self):
744 _ = self.request.translate
745 c = self.load_default_context()
746
747 user_id = self.db_user_id
748 c.user = self.db_user
749 user_notice_id = safe_int(self.request.POST.get('notice_id'))
750 notice = UserNotice().query()\
751 .filter(UserNotice.user_id == user_id)\
752 .filter(UserNotice.user_notice_id == user_notice_id)\
753 .scalar()
754 read = False
755 if notice:
756 notice.notice_read = True
757 Session().add(notice)
758 Session().commit()
759 read = True
760
761 return {'notice': user_notice_id, 'read': read}
762
763 @LoginRequired()
764 @HasPermissionAllDecorator('hg.admin')
765 @CSRFRequired()
766 @view_config(
708 route_name='user_create_personal_repo_group', request_method='POST',
767 route_name='user_create_personal_repo_group', request_method='POST',
709 renderer='rhodecode:templates/admin/users/user_edit.mako')
768 renderer='rhodecode:templates/admin/users/user_edit.mako')
710 def user_create_personal_repo_group(self):
769 def user_create_personal_repo_group(self):
@@ -778,6 +837,25 b' class UsersView(UserAppView):'
778 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
837 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
779 return self._get_template_context(c)
838 return self._get_template_context(c)
780
839
840 @LoginRequired()
841 @HasPermissionAllDecorator('hg.admin')
842 @view_config(
843 route_name='edit_user_auth_tokens_view', request_method='POST',
844 renderer='json_ext', xhr=True)
845 def auth_tokens_view(self):
846 _ = self.request.translate
847 c = self.load_default_context()
848 c.user = self.db_user
849
850 auth_token_id = self.request.POST.get('auth_token_id')
851
852 if auth_token_id:
853 token = UserApiKeys.get_or_404(auth_token_id)
854
855 return {
856 'auth_token': token.api_key
857 }
858
781 def maybe_attach_token_scope(self, token):
859 def maybe_attach_token_scope(self, token):
782 # implemented in EE edition
860 # implemented in EE edition
783 pass
861 pass
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -88,6 +88,15 b' Check if we should use full-topic or min'
88 'modified': ['b/modified_file.rst'],
88 'modified': ['b/modified_file.rst'],
89 'removed': ['.idea'],
89 'removed': ['.idea'],
90 })
90 })
91
92 exc_traceback = {
93 'exc_utc_date': '2020-03-26T12:54:50.683281',
94 'exc_id': 139638856342656,
95 'exc_timestamp': '1585227290.683288',
96 'version': 'v1',
97 'exc_message': 'Traceback (most recent call last):\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/tweens.py", line 41, in excview_tween\n response = handler(request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/router.py", line 148, in handle_request\n registry, request, context, context_iface, view_name\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/view.py", line 667, in _call_view\n response = view_callable(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 188, in attr_view\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 214, in predicate_wrapper\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 401, in viewresult_to_response\n result = view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 132, in _class_view\n response = getattr(inst, attr)()\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/apps/debug_style/views.py", line 355, in render_email\n template_type, **email_kwargs.get(email_id, {}))\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/model/notification.py", line 402, in render_email\n body = email_template.render(None, **_kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 95, in render\n return self._render_with_exc(tmpl, args, kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 79, in _render_with_exc\n return render_func.render(*args, **kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/template.py", line 476, in render\n return runtime._render(self, self.callable_, args, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 883, in _render\n **_kwargs_for_callable(callable_, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 920, in _render_context\n _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 947, in _exec_template\n callable_(context, *args, **kwargs)\n File "rhodecode_templates_email_templates_base_mako", line 63, in render_body\n File "rhodecode_templates_email_templates_exception_tracker_mako", line 43, in render_body\nAttributeError: \'str\' object has no attribute \'get\'\n',
98 'exc_type': 'AttributeError'
99 }
91 email_kwargs = {
100 email_kwargs = {
92 'test': {},
101 'test': {},
93 'message': {
102 'message': {
@@ -97,6 +106,13 b' Check if we should use full-topic or min'
97 'user': user,
106 'user': user,
98 'date': datetime.datetime.now(),
107 'date': datetime.datetime.now(),
99 },
108 },
109 'exception': {
110 'email_prefix': '[RHODECODE ERROR]',
111 'exc_id': exc_traceback['exc_id'],
112 'exc_url': 'http://server-url/{}'.format(exc_traceback['exc_id']),
113 'exc_type_name': 'NameError',
114 'exc_traceback': exc_traceback,
115 },
100 'password_reset': {
116 'password_reset': {
101 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
117 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
102
118
@@ -204,7 +220,7 b' def db():'
204 'pr_comment_url': 'http://comment-url',
220 'pr_comment_url': 'http://comment-url',
205 'pr_comment_reply_url': 'http://comment-url#reply',
221 'pr_comment_reply_url': 'http://comment-url#reply',
206
222
207 'comment_file': 'rhodecode/model/db.py',
223 'comment_file': 'rhodecode/model/get_flow_commits',
208 'comment_line': 'o1210',
224 'comment_line': 'o1210',
209 'comment_type': 'todo',
225 'comment_type': 'todo',
210 'comment_body': '''
226 'comment_body': '''
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +19,7 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 # -*- coding: utf-8 -*-
20 # -*- coding: utf-8 -*-
21
21
22 # Copyright (C) 2016-2019 RhodeCode GmbH
22 # Copyright (C) 2016-2020 RhodeCode GmbH
23 #
23 #
24 # This program is free software: you can redistribute it and/or modify
24 # This program is free software: you can redistribute it and/or modify
25 # it under the terms of the GNU Affero General Public License, version 3
25 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -35,8 +35,8 b' from rhodecode.lib.index import searcher'
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
36 from rhodecode.lib.vcs.nodes import FileNode
36 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.model.db import (
37 from rhodecode.model.db import (
38 func, true, or_, case, in_filter_generator, Session,
38 func, true, or_, case, cast, in_filter_generator, String, Session,
39 Repository, RepoGroup, User, UserGroup)
39 Repository, RepoGroup, User, UserGroup, PullRequest)
40 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo_group import RepoGroupModel
41 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
@@ -111,7 +111,7 b' class HomeView(BaseAppView, DataGridAppV'
111 org_query = name_contains
111 org_query = name_contains
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
113 ['repository.read', 'repository.write', 'repository.admin'],
113 ['repository.read', 'repository.write', 'repository.admin'],
114 cache=False, name_filter=name_contains) or [-1]
114 cache=True, name_filter=name_contains) or [-1]
115
115
116 query = Session().query(
116 query = Session().query(
117 Repository.repo_name,
117 Repository.repo_name,
@@ -162,7 +162,7 b' class HomeView(BaseAppView, DataGridAppV'
162 org_query = name_contains
162 org_query = name_contains
163 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
163 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
164 ['group.read', 'group.write', 'group.admin'],
164 ['group.read', 'group.write', 'group.admin'],
165 cache=False, name_filter=name_contains) or [-1]
165 cache=True, name_filter=name_contains) or [-1]
166
166
167 query = Session().query(
167 query = Session().query(
168 RepoGroup.group_id,
168 RepoGroup.group_id,
@@ -282,6 +282,61 b' class HomeView(BaseAppView, DataGridAppV'
282 }
282 }
283 for obj in acl_iter], True
283 for obj in acl_iter], True
284
284
285 def _get_pull_request_list(self, name_contains=None, limit=20):
286 org_query = name_contains
287 if not name_contains:
288 return [], False
289
290 # TODO(marcink): should all logged in users be allowed to search others?
291 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
292 if not allowed_user_search:
293 return [], False
294
295 name_contains = re.compile('(?:pr:[ ]?)(.+)').findall(name_contains)
296 if len(name_contains) != 1:
297 return [], False
298
299 name_contains = name_contains[0]
300
301 allowed_ids = self._rhodecode_user.repo_acl_ids(
302 ['repository.read', 'repository.write', 'repository.admin'],
303 cache=True) or [-1]
304
305 query = Session().query(
306 PullRequest.pull_request_id,
307 PullRequest.title,
308 )
309 query = query.join(Repository, Repository.repo_id == PullRequest.target_repo_id)
310
311 query = query.filter(or_(
312 # generate multiple IN to fix limitation problems
313 *in_filter_generator(Repository.repo_id, allowed_ids)
314 ))
315
316 query = query.order_by(PullRequest.pull_request_id)
317
318 if name_contains:
319 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
320 query = query.filter(or_(
321 cast(PullRequest.pull_request_id, String).ilike(ilike_expression),
322 PullRequest.title.ilike(ilike_expression),
323 PullRequest.description.ilike(ilike_expression),
324 ))
325
326 query = query.limit(limit)
327
328 acl_iter = query
329
330 return [
331 {
332 'id': obj.pull_request_id,
333 'value': org_query,
334 'value_display': 'pull request: `!{} - {}`'.format(obj.pull_request_id, obj.title[:50]),
335 'type': 'pull_request',
336 'url': h.route_path('pull_requests_global', pull_request_id=obj.pull_request_id)
337 }
338 for obj in acl_iter], True
339
285 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
340 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
286 repo_name = repo_group_name = None
341 repo_name = repo_group_name = None
287 if repo:
342 if repo:
@@ -624,6 +679,17 b' class HomeView(BaseAppView, DataGridAppV'
624 has_specialized_search = True
679 has_specialized_search = True
625 res.append(no_match('No matching user groups found'))
680 res.append(no_match('No matching user groups found'))
626
681
682 # pr: type search
683 if not prefix_match:
684 pull_requests, prefix_match = self._get_pull_request_list(query)
685 if pull_requests:
686 has_specialized_search = True
687 for serialized_pull_request in pull_requests:
688 res.append(serialized_pull_request)
689 elif prefix_match:
690 has_specialized_search = True
691 res.append(no_match('No matching pull requests found'))
692
627 # FTS commit: type search
693 # FTS commit: type search
628 if not prefix_match:
694 if not prefix_match:
629 commits, prefix_match = self._get_hash_commit_list(
695 commits, prefix_match = self._get_hash_commit_list(
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2018-2019 RhodeCode GmbH
3 # Copyright (C) 2018-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -50,6 +50,9 b' def includeme(config):'
50 name='my_account_auth_tokens',
50 name='my_account_auth_tokens',
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
52 config.add_route(
52 config.add_route(
53 name='my_account_auth_tokens_view',
54 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/view')
55 config.add_route(
53 name='my_account_auth_tokens_add',
56 name='my_account_auth_tokens_add',
54 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
57 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
55 config.add_route(
58 config.add_route(
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,7 +49,7 b' class TestMyAccountAuthTokens(TestContro'
49 user = User.get(usr['user_id'])
49 user = User.get(usr['user_id'])
50 response = self.app.get(route_path('my_account_auth_tokens'))
50 response = self.app.get(route_path('my_account_auth_tokens'))
51 for token in user.auth_tokens:
51 for token in user.auth_tokens:
52 response.mustcontain(token)
52 response.mustcontain(token[:4])
53 response.mustcontain('never')
53 response.mustcontain('never')
54
54
55 def test_my_account_add_auth_tokens_wrong_csrf(self, user_util):
55 def test_my_account_add_auth_tokens_wrong_csrf(self, user_util):
@@ -79,7 +79,7 b' class TestMyAccountAuthTokens(TestContro'
79 response = response.follow()
79 response = response.follow()
80 user = User.get(user_id)
80 user = User.get(user_id)
81 for auth_token in user.auth_tokens:
81 for auth_token in user.auth_tokens:
82 response.mustcontain(auth_token)
82 response.mustcontain(auth_token[:4])
83
83
84 def test_my_account_delete_auth_token(self, user_util):
84 def test_my_account_delete_auth_token(self, user_util):
85 user = user_util.create_user(password='qweqwe')
85 user = user_util.create_user(password='qweqwe')
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +19,7 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 # -*- coding: utf-8 -*-
20 # -*- coding: utf-8 -*-
21
21
22 # Copyright (C) 2016-2019 RhodeCode GmbH
22 # Copyright (C) 2016-2020 RhodeCode GmbH
23 #
23 #
24 # This program is free software: you can redistribute it and/or modify
24 # This program is free software: you can redistribute it and/or modify
25 # it under the terms of the GNU Affero General Public License, version 3
25 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,7 +25,7 b' import string'
25 import formencode
25 import formencode
26 import formencode.htmlfill
26 import formencode.htmlfill
27 import peppercorn
27 import peppercorn
28 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
29 from pyramid.view import view_config
29 from pyramid.view import view_config
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
@@ -164,6 +164,27 b' class MyAccountView(BaseAppView, DataGri'
164 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
164 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
165 return self._get_template_context(c)
165 return self._get_template_context(c)
166
166
167 @LoginRequired()
168 @NotAnonymous()
169 @CSRFRequired()
170 @view_config(
171 route_name='my_account_auth_tokens_view', request_method='POST', xhr=True,
172 renderer='json_ext')
173 def my_account_auth_tokens_view(self):
174 _ = self.request.translate
175 c = self.load_default_context()
176
177 auth_token_id = self.request.POST.get('auth_token_id')
178
179 if auth_token_id:
180 token = UserApiKeys.get_or_404(auth_token_id)
181 if token.user.user_id != c.user.user_id:
182 raise HTTPNotFound()
183
184 return {
185 'auth_token': token.api_key
186 }
187
167 def maybe_attach_token_scope(self, token):
188 def maybe_attach_token_scope(self, token):
168 # implemented in EE edition
189 # implemented in EE edition
169 pass
190 pass
@@ -702,12 +723,12 b' class MyAccountView(BaseAppView, DataGri'
702
723
703 pull_requests = PullRequestModel().get_im_participating_in(
724 pull_requests = PullRequestModel().get_im_participating_in(
704 user_id=self._rhodecode_user.user_id,
725 user_id=self._rhodecode_user.user_id,
705 statuses=statuses,
726 statuses=statuses, query=search_q,
706 offset=start, length=limit, order_by=order_by,
727 offset=start, length=limit, order_by=order_by,
707 order_dir=order_dir)
728 order_dir=order_dir)
708
729
709 pull_requests_total_count = PullRequestModel().count_im_participating_in(
730 pull_requests_total_count = PullRequestModel().count_im_participating_in(
710 user_id=self._rhodecode_user.user_id, statuses=statuses)
731 user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
711
732
712 data = []
733 data = []
713 comments_model = CommentsModel()
734 comments_model = CommentsModel()
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -156,6 +156,10 b' def includeme(config):'
156 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
156 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
157
157
158 config.add_route(
158 config.add_route(
159 name='repo_files_check_head',
160 pattern='/{repo_name:.*?[^/]}/check_head/{commit_id}/{f_path:.*}',
161 repo_route=True)
162 config.add_route(
159 name='repo_files_remove_file',
163 name='repo_files_remove_file',
160 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
164 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
161 repo_route=True)
165 repo_route=True)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,8 +22,7 b' import pytest'
22
22
23 from rhodecode.tests import TestController
23 from rhodecode.tests import TestController
24
24
25 from rhodecode.model.db import (
25 from rhodecode.model.db import ChangesetComment, Notification
26 ChangesetComment, Notification, UserNotification)
27 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
28 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
29
28
@@ -269,7 +268,36 b' class TestRepoCommitCommentsView(TestCon'
269 repo_name=backend.repo_name, commit_id=commit_id))
268 repo_name=backend.repo_name, commit_id=commit_id))
270 assert_comment_links(response, 0, 0)
269 assert_comment_links(response, 0, 0)
271
270
272 @pytest.mark.parametrize('renderer, input, output', [
271 def test_delete_forbidden_for_immutable_comments(self, backend):
272 self.log_user()
273 commit_id = backend.repo.get_commit('300').raw_id
274 text = u'CommentOnCommit'
275
276 params = {'text': text, 'csrf_token': self.csrf_token}
277 self.app.post(
278 route_path(
279 'repo_commit_comment_create',
280 repo_name=backend.repo_name, commit_id=commit_id),
281 params=params)
282
283 comments = ChangesetComment.query().all()
284 assert len(comments) == 1
285 comment_id = comments[0].comment_id
286
287 comment = ChangesetComment.get(comment_id)
288 comment.immutable_state = ChangesetComment.OP_IMMUTABLE
289 Session().add(comment)
290 Session().commit()
291
292 self.app.post(
293 route_path('repo_commit_comment_delete',
294 repo_name=backend.repo_name,
295 commit_id=commit_id,
296 comment_id=comment_id),
297 params={'csrf_token': self.csrf_token},
298 status=403)
299
300 @pytest.mark.parametrize('renderer, text_input, output', [
273 ('rst', 'plain text', '<p>plain text</p>'),
301 ('rst', 'plain text', '<p>plain text</p>'),
274 ('rst', 'header\n======', '<h1 class="title">header</h1>'),
302 ('rst', 'header\n======', '<h1 class="title">header</h1>'),
275 ('rst', '*italics*', '<em>italics</em>'),
303 ('rst', '*italics*', '<em>italics</em>'),
@@ -280,11 +308,11 b' class TestRepoCommitCommentsView(TestCon'
280 ('markdown', '**bold**', '<strong>bold</strong>'),
308 ('markdown', '**bold**', '<strong>bold</strong>'),
281 ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
309 ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
282 'md-header', 'md-italics', 'md-bold', ])
310 'md-header', 'md-italics', 'md-bold', ])
283 def test_preview(self, renderer, input, output, backend, xhr_header):
311 def test_preview(self, renderer, text_input, output, backend, xhr_header):
284 self.log_user()
312 self.log_user()
285 params = {
313 params = {
286 'renderer': renderer,
314 'renderer': renderer,
287 'text': input,
315 'text': text_input,
288 'csrf_token': self.csrf_token
316 'csrf_token': self.csrf_token
289 }
317 }
290 commit_id = '0' * 16 # fake this for tests
318 commit_id = '0' * 16 # fake this for tests
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -484,7 +484,7 b' class TestCompareView(object):'
484
484
485 # outgoing commits between those commits
485 # outgoing commits between those commits
486 compare_page = ComparePage(response)
486 compare_page = ComparePage(response)
487 compare_page.contains_commits(commits=[commit1], ancestors=[commit0])
487 compare_page.contains_commits(commits=[commit1])
488
488
489 def test_errors_when_comparing_unknown_source_repo(self, backend):
489 def test_errors_when_comparing_unknown_source_repo(self, backend):
490 repo = backend.repo
490 repo = backend.repo
@@ -641,6 +641,7 b' class ComparePage(AssertResponse):'
641 self.contains_one_link(
641 self.contains_one_link(
642 'r%s:%s' % (commit.idx, commit.short_id),
642 'r%s:%s' % (commit.idx, commit.short_id),
643 self._commit_url(commit))
643 self._commit_url(commit))
644
644 if ancestors:
645 if ancestors:
645 response.mustcontain('Ancestor')
646 response.mustcontain('Ancestor')
646 for ancestor in ancestors:
647 for ancestor in ancestors:
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -629,7 +629,7 b' class TestPullrequestsView(object):'
629 model_patcher = mock.patch.multiple(
629 model_patcher = mock.patch.multiple(
630 PullRequestModel,
630 PullRequestModel,
631 merge_repo=mock.Mock(return_value=merge_resp),
631 merge_repo=mock.Mock(return_value=merge_resp),
632 merge_status=mock.Mock(return_value=(True, 'WRONG_MESSAGE')))
632 merge_status=mock.Mock(return_value=(None, True, 'WRONG_MESSAGE')))
633
633
634 with model_patcher:
634 with model_patcher:
635 response = self.app.post(
635 response = self.app.post(
@@ -891,6 +891,8 b' class TestPullrequestsView(object):'
891
891
892 vcs = repo.scm_instance()
892 vcs = repo.scm_instance()
893 vcs.remove_ref('refs/heads/{}'.format(branch_name))
893 vcs.remove_ref('refs/heads/{}'.format(branch_name))
894 # NOTE(marcink): run GC to ensure the commits are gone
895 vcs.run_gc()
894
896
895 response = self.app.get(route_path(
897 response = self.app.get(route_path(
896 'pullrequest_show',
898 'pullrequest_show',
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,6 +20,9 b''
20
20
21 from rhodecode.lib import helpers as h
21 from rhodecode.lib import helpers as h
22 from rhodecode.lib.utils2 import safe_int
22 from rhodecode.lib.utils2 import safe_int
23 from rhodecode.model.pull_request import get_diff_info
24
25 REVIEWER_API_VERSION = 'V3'
23
26
24
27
25 def reviewer_as_json(user, reasons=None, mandatory=False, rules=None, user_group=None):
28 def reviewer_as_json(user, reasons=None, mandatory=False, rules=None, user_group=None):
@@ -47,15 +50,20 b' def reviewer_as_json(user, reasons=None,'
47
50
48 def get_default_reviewers_data(
51 def get_default_reviewers_data(
49 current_user, source_repo, source_commit, target_repo, target_commit):
52 current_user, source_repo, source_commit, target_repo, target_commit):
53 """
54 Return json for default reviewers of a repository
55 """
50
56
51 """ Return json for default reviewers of a repository """
57 diff_info = get_diff_info(
58 source_repo, source_commit.raw_id, target_repo, target_commit.raw_id)
52
59
53 reasons = ['Default reviewer', 'Repository owner']
60 reasons = ['Default reviewer', 'Repository owner']
54 json_reviewers = [reviewer_as_json(
61 json_reviewers = [reviewer_as_json(
55 user=target_repo.user, reasons=reasons, mandatory=False, rules=None)]
62 user=target_repo.user, reasons=reasons, mandatory=False, rules=None)]
56
63
57 return {
64 return {
58 'api_ver': 'v1', # define version for later possible schema upgrade
65 'api_ver': REVIEWER_API_VERSION, # define version for later possible schema upgrade
66 'diff_info': diff_info,
59 'reviewers': json_reviewers,
67 'reviewers': json_reviewers,
60 'rules': {},
68 'rules': {},
61 'rules_data': {},
69 'rules_data': {},
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 b''
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
@@ -380,6 +380,11 b' class RepoCommitsView(RepoAppView):'
380 'repo_commit', repo_name=self.db_repo_name,
380 'repo_commit', repo_name=self.db_repo_name,
381 commit_id=current_id))
381 commit_id=current_id))
382
382
383 commit = self.db_repo.get_commit(current_id)
384 CommentsModel().trigger_commit_comment_hook(
385 self.db_repo, self._rhodecode_user, 'create',
386 data={'comment': comment, 'commit': commit})
387
383 # finalize, commit and redirect
388 # finalize, commit and redirect
384 Session().commit()
389 Session().commit()
385
390
@@ -533,6 +538,10 b' class RepoCommitsView(RepoAppView):'
533 # comment already deleted in another call probably
538 # comment already deleted in another call probably
534 return True
539 return True
535
540
541 if comment.immutable:
542 # don't allow deleting comments that are immutable
543 raise HTTPForbidden()
544
536 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
545 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
537 super_admin = h.HasPermissionAny('hg.admin')()
546 super_admin = h.HasPermissionAny('hg.admin')()
538 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
547 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -245,7 +245,7 b' class RepoFilesView(RepoAppView):'
245
245
246 return branch_name, sha_commit_id, is_head
246 return branch_name, sha_commit_id, is_head
247
247
248 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False):
248 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None):
249
249
250 repo_id = self.db_repo.repo_id
250 repo_id = self.db_repo.repo_id
251 force_recache = self.get_recache_flag()
251 force_recache = self.get_recache_flag()
@@ -261,19 +261,19 b' class RepoFilesView(RepoAppView):'
261 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
261 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
262 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
262 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
263
263
264 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
264 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
265 condition=cache_on)
265 def compute_file_tree(ver, _name_hash, _repo_id, _commit_id, _f_path, _full_load, _at_rev):
266 def compute_file_tree(ver, repo_id, commit_id, f_path, full_load):
267 log.debug('Generating cached file tree at ver:%s for repo_id: %s, %s, %s',
266 log.debug('Generating cached file tree at ver:%s for repo_id: %s, %s, %s',
268 ver, repo_id, commit_id, f_path)
267 ver, _repo_id, _commit_id, _f_path)
269
268
270 c.full_load = full_load
269 c.full_load = _full_load
271 return render(
270 return render(
272 'rhodecode:templates/files/files_browser_tree.mako',
271 'rhodecode:templates/files/files_browser_tree.mako',
273 self._get_template_context(c), self.request)
272 self._get_template_context(c), self.request, _at_rev)
274
273
275 return compute_file_tree(
274 return compute_file_tree(
276 rc_cache.FILE_TREE_CACHE_VER, self.db_repo.repo_id, commit_id, f_path, full_load)
275 rc_cache.FILE_TREE_CACHE_VER, self.db_repo.repo_name_hash,
276 self.db_repo.repo_id, commit_id, f_path, full_load, at_rev)
277
277
278 def _get_archive_spec(self, fname):
278 def _get_archive_spec(self, fname):
279 log.debug('Detecting archive spec for: `%s`', fname)
279 log.debug('Detecting archive spec for: `%s`', fname)
@@ -617,15 +617,12 b' class RepoFilesView(RepoAppView):'
617 c.renderer = view_name == 'repo_files:rendered' or \
617 c.renderer = view_name == 'repo_files:rendered' or \
618 not self.request.GET.get('no-render', False)
618 not self.request.GET.get('no-render', False)
619
619
620 # redirect to given commit_id from form if given
620 commit_id, f_path = self._get_commit_and_path()
621 get_commit_id = self.request.GET.get('at_rev', None)
622 if get_commit_id:
623 self._get_commit_or_redirect(get_commit_id)
624
621
625 commit_id, f_path = self._get_commit_and_path()
626 c.commit = self._get_commit_or_redirect(commit_id)
622 c.commit = self._get_commit_or_redirect(commit_id)
627 c.branch = self.request.GET.get('branch', None)
623 c.branch = self.request.GET.get('branch', None)
628 c.f_path = f_path
624 c.f_path = f_path
625 at_rev = self.request.GET.get('at')
629
626
630 # prev link
627 # prev link
631 try:
628 try:
@@ -705,7 +702,7 b' class RepoFilesView(RepoAppView):'
705 c.authors = []
702 c.authors = []
706 # this loads a simple tree without metadata to speed things up
703 # this loads a simple tree without metadata to speed things up
707 # later via ajax we call repo_nodetree_full and fetch whole
704 # later via ajax we call repo_nodetree_full and fetch whole
708 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path)
705 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
709
706
710 c.readme_data, c.readme_file = \
707 c.readme_data, c.readme_file = \
711 self._get_readme_data(self.db_repo, c.visual.default_renderer,
708 self._get_readme_data(self.db_repo, c.visual.default_renderer,
@@ -782,9 +779,10 b' class RepoFilesView(RepoAppView):'
782
779
783 c.file = dir_node
780 c.file = dir_node
784 c.commit = commit
781 c.commit = commit
782 at_rev = self.request.GET.get('at')
785
783
786 html = self._get_tree_at_commit(
784 html = self._get_tree_at_commit(
787 c, commit.raw_id, dir_node.path, full_load=True)
785 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
788
786
789 return Response(html)
787 return Response(html)
790
788
@@ -915,13 +913,12 b' class RepoFilesView(RepoAppView):'
915 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
913 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
916 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
914 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
917
915
918 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
916 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
919 condition=cache_on)
917 def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path):
920 def compute_file_search(repo_id, commit_id, f_path):
921 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
918 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
922 repo_id, commit_id, f_path)
919 _repo_id, commit_id, f_path)
923 try:
920 try:
924 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, commit_id, f_path)
921 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
925 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
922 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
926 log.exception(safe_str(e))
923 log.exception(safe_str(e))
927 h.flash(safe_str(h.escape(e)), category='error')
924 h.flash(safe_str(h.escape(e)), category='error')
@@ -931,7 +928,8 b' class RepoFilesView(RepoAppView):'
931
928
932 return _d + _f
929 return _d + _f
933
930
934 result = compute_file_search(self.db_repo.repo_id, commit_id, f_path)
931 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id,
932 commit_id, f_path)
935 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
933 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
936
934
937 @LoginRequired()
935 @LoginRequired()
@@ -961,6 +959,9 b' class RepoFilesView(RepoAppView):'
961 return commit_id
959 return commit_id
962
960
963 def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type):
961 def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type):
962 return commit_id
963
964 # NOTE(dan): old code we used in "diff" mode compare
964 new_f_path = vcspath.join(name, f_path)
965 new_f_path = vcspath.join(name, f_path)
965 return u'%s@%s' % (new_f_path, commit_id)
966 return u'%s@%s' % (new_f_path, commit_id)
966
967
@@ -1035,10 +1036,24 b' class RepoFilesView(RepoAppView):'
1035 file_history, _hist = self._get_node_history(commit, f_path)
1036 file_history, _hist = self._get_node_history(commit, f_path)
1036
1037
1037 res = []
1038 res = []
1038 for obj in file_history:
1039 for section_items, section in file_history:
1040 items = []
1041 for obj_id, obj_text, obj_type in section_items:
1042 at_rev = ''
1043 if obj_type in ['branch', 'bookmark', 'tag']:
1044 at_rev = obj_text
1045 entry = {
1046 'id': obj_id,
1047 'text': obj_text,
1048 'type': obj_type,
1049 'at_rev': at_rev
1050 }
1051
1052 items.append(entry)
1053
1039 res.append({
1054 res.append({
1040 'text': obj[1],
1055 'text': section,
1041 'children': [{'id': o[0], 'text': o[1], 'type': o[2]} for o in obj[0]]
1056 'children': items
1042 })
1057 })
1043
1058
1044 data = {
1059 data = {
@@ -1098,6 +1113,42 b' class RepoFilesView(RepoAppView):'
1098 @LoginRequired()
1113 @LoginRequired()
1099 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1114 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1100 @view_config(
1115 @view_config(
1116 route_name='repo_files_check_head', request_method='POST',
1117 renderer='json_ext', xhr=True)
1118 def repo_files_check_head(self):
1119 self.load_default_context()
1120
1121 commit_id, f_path = self._get_commit_and_path()
1122 _branch_name, _sha_commit_id, is_head = \
1123 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1124
1125 new_path = self.request.POST.get('path')
1126 operation = self.request.POST.get('operation')
1127 path_exist = ''
1128
1129 if new_path and operation in ['create', 'upload']:
1130 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1131 try:
1132 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1133 # NOTE(dan): construct whole path without leading /
1134 file_node = commit_obj.get_node(new_f_path)
1135 if file_node is not None:
1136 path_exist = new_f_path
1137 except EmptyRepositoryError:
1138 pass
1139 except Exception:
1140 pass
1141
1142 return {
1143 'branch': _branch_name,
1144 'sha': _sha_commit_id,
1145 'is_head': is_head,
1146 'path_exists': path_exist
1147 }
1148
1149 @LoginRequired()
1150 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1151 @view_config(
1101 route_name='repo_files_remove_file', request_method='GET',
1152 route_name='repo_files_remove_file', request_method='GET',
1102 renderer='rhodecode:templates/files/files_delete.mako')
1153 renderer='rhodecode:templates/files/files_delete.mako')
1103 def repo_files_remove_file(self):
1154 def repo_files_remove_file(self):
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -114,21 +114,26 b' class RepoSettingsPermissionsView(RepoAp'
114 private_flag = str2bool(self.request.POST.get('private'))
114 private_flag = str2bool(self.request.POST.get('private'))
115
115
116 try:
116 try:
117 RepoModel().update(
117 repo = RepoModel().get(self.db_repo.repo_id)
118 self.db_repo, **{'repo_private': private_flag, 'repo_name': self.db_repo_name})
118 repo.private = private_flag
119 Session().add(repo)
120 RepoModel().grant_user_permission(
121 repo=self.db_repo, user=User.DEFAULT_USER, perm='repository.none'
122 )
123
119 Session().commit()
124 Session().commit()
120
125
121 h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name),
126 h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name),
122 category='success')
127 category='success')
128 # NOTE(dan): we change repo private mode we need to notify all USERS
129 affected_user_ids = User.get_all_user_ids()
130 PermissionModel().trigger_permission_flush(affected_user_ids)
131
123 except Exception:
132 except Exception:
124 log.exception("Exception during update of repository")
133 log.exception("Exception during update of repository")
125 h.flash(_('Error occurred during update of repository {}').format(
134 h.flash(_('Error occurred during update of repository {}').format(
126 self.db_repo_name), category='error')
135 self.db_repo_name), category='error')
127
136
128 # NOTE(dan): we change repo private mode we need to notify all USERS
129 affected_user_ids = User.get_all_user_ids()
130 PermissionModel().trigger_permission_flush(affected_user_ids)
131
132 return {
137 return {
133 'redirect_url': h.route_path('edit_repo_perms', repo_name=self.db_repo_name),
138 'redirect_url': h.route_path('edit_repo_perms', repo_name=self.db_repo_name),
134 'private': private_flag
139 'private': private_flag
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -40,12 +40,12 b' from rhodecode.lib.auth import ('
40 NotAnonymous, CSRFRequired)
40 NotAnonymous, CSRFRequired)
41 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode
41 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode
42 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason
42 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason
43 from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError,
43 from rhodecode.lib.vcs.exceptions import (
44 RepositoryRequirementError, EmptyRepositoryError)
44 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
45 from rhodecode.model.changeset_status import ChangesetStatusModel
45 from rhodecode.model.changeset_status import ChangesetStatusModel
46 from rhodecode.model.comment import CommentsModel
46 from rhodecode.model.comment import CommentsModel
47 from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion,
47 from rhodecode.model.db import (
48 ChangesetComment, ChangesetStatus, Repository)
48 func, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository)
49 from rhodecode.model.forms import PullRequestForm
49 from rhodecode.model.forms import PullRequestForm
50 from rhodecode.model.meta import Session
50 from rhodecode.model.meta import Session
51 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
51 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
@@ -210,10 +210,12 b' class RepoPullRequestsView(RepoAppView, '
210 return caching_enabled
210 return caching_enabled
211
211
212 def _get_diffset(self, source_repo_name, source_repo,
212 def _get_diffset(self, source_repo_name, source_repo,
213 ancestor_commit,
213 source_ref_id, target_ref_id,
214 source_ref_id, target_ref_id,
214 target_commit, source_commit, diff_limit, file_limit,
215 target_commit, source_commit, diff_limit, file_limit,
215 fulldiff, hide_whitespace_changes, diff_context):
216 fulldiff, hide_whitespace_changes, diff_context):
216
217
218 target_ref_id = ancestor_commit.raw_id
217 vcs_diff = PullRequestModel().get_diff(
219 vcs_diff = PullRequestModel().get_diff(
218 source_repo, source_ref_id, target_ref_id,
220 source_repo, source_ref_id, target_ref_id,
219 hide_whitespace_changes, diff_context)
221 hide_whitespace_changes, diff_context)
@@ -278,6 +280,7 b' class RepoPullRequestsView(RepoAppView, '
278 _new_state = {
280 _new_state = {
279 'created': PullRequest.STATE_CREATED,
281 'created': PullRequest.STATE_CREATED,
280 }.get(self.request.GET.get('force_state'))
282 }.get(self.request.GET.get('force_state'))
283
281 if c.is_super_admin and _new_state:
284 if c.is_super_admin and _new_state:
282 with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
285 with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
283 h.flash(
286 h.flash(
@@ -396,9 +399,12 b' class RepoPullRequestsView(RepoAppView, '
396 pull_request_latest, auth_user=self._rhodecode_user,
399 pull_request_latest, auth_user=self._rhodecode_user,
397 translator=self.request.translate,
400 translator=self.request.translate,
398 force_shadow_repo_refresh=force_refresh)
401 force_shadow_repo_refresh=force_refresh)
402
399 c.pr_merge_errors = _merge_check.error_details
403 c.pr_merge_errors = _merge_check.error_details
400 c.pr_merge_possible = not _merge_check.failed
404 c.pr_merge_possible = not _merge_check.failed
401 c.pr_merge_message = _merge_check.merge_msg
405 c.pr_merge_message = _merge_check.merge_msg
406 c.pr_merge_source_commit = _merge_check.source_commit
407 c.pr_merge_target_commit = _merge_check.target_commit
402
408
403 c.pr_merge_info = MergeCheck.get_merge_conditions(
409 c.pr_merge_info = MergeCheck.get_merge_conditions(
404 pull_request_latest, translator=self.request.translate)
410 pull_request_latest, translator=self.request.translate)
@@ -537,6 +543,13 b' class RepoPullRequestsView(RepoAppView, '
537 (ancestor_commit, commit_cache, missing_requirements,
543 (ancestor_commit, commit_cache, missing_requirements,
538 source_commit, target_commit) = cached_diff['commits']
544 source_commit, target_commit) = cached_diff['commits']
539 else:
545 else:
546 # NOTE(marcink): we reach potentially unreachable errors when a PR has
547 # merge errors resulting in potentially hidden commits in the shadow repo.
548 maybe_unreachable = _merge_check.MERGE_CHECK in _merge_check.error_details \
549 and _merge_check.merge_response
550 maybe_unreachable = maybe_unreachable \
551 and _merge_check.merge_response.metadata.get('unresolved_files')
552 log.debug("Using unreachable commits due to MERGE_CHECK in merge simulation")
540 diff_commit_cache = \
553 diff_commit_cache = \
541 (ancestor_commit, commit_cache, missing_requirements,
554 (ancestor_commit, commit_cache, missing_requirements,
542 source_commit, target_commit) = self.get_commits(
555 source_commit, target_commit) = self.get_commits(
@@ -547,7 +560,8 b' class RepoPullRequestsView(RepoAppView, '
547 source_scm,
560 source_scm,
548 target_commit,
561 target_commit,
549 target_ref_id,
562 target_ref_id,
550 target_scm)
563 target_scm,
564 maybe_unreachable=maybe_unreachable)
551
565
552 # register our commit range
566 # register our commit range
553 for comm in commit_cache.values():
567 for comm in commit_cache.values():
@@ -581,11 +595,10 b' class RepoPullRequestsView(RepoAppView, '
581 has_proper_diff_cache = cached_diff and cached_diff.get('commits')
595 has_proper_diff_cache = cached_diff and cached_diff.get('commits')
582 if not force_recache and has_proper_diff_cache:
596 if not force_recache and has_proper_diff_cache:
583 c.diffset = cached_diff['diff']
597 c.diffset = cached_diff['diff']
584 (ancestor_commit, commit_cache, missing_requirements,
585 source_commit, target_commit) = cached_diff['commits']
586 else:
598 else:
587 c.diffset = self._get_diffset(
599 c.diffset = self._get_diffset(
588 c.source_repo.repo_name, commits_source_repo,
600 c.source_repo.repo_name, commits_source_repo,
601 c.ancestor_commit,
589 source_ref_id, target_ref_id,
602 source_ref_id, target_ref_id,
590 target_commit, source_commit,
603 target_commit, source_commit,
591 diff_limit, file_limit, c.fulldiff,
604 diff_limit, file_limit, c.fulldiff,
@@ -665,8 +678,10 b' class RepoPullRequestsView(RepoAppView, '
665
678
666 # calculate the diff for commits between versions
679 # calculate the diff for commits between versions
667 c.commit_changes = []
680 c.commit_changes = []
668 mark = lambda cs, fw: list(
681
669 h.itertools.izip_longest([], cs, fillvalue=fw))
682 def mark(cs, fw):
683 return list(h.itertools.izip_longest([], cs, fillvalue=fw))
684
670 for c_type, raw_id in mark(commit_changes.added, 'a') \
685 for c_type, raw_id in mark(commit_changes.added, 'a') \
671 + mark(commit_changes.removed, 'r') \
686 + mark(commit_changes.removed, 'r') \
672 + mark(commit_changes.common, 'c'):
687 + mark(commit_changes.common, 'c'):
@@ -698,15 +713,22 b' class RepoPullRequestsView(RepoAppView, '
698
713
699 def get_commits(
714 def get_commits(
700 self, commits_source_repo, pull_request_at_ver, source_commit,
715 self, commits_source_repo, pull_request_at_ver, source_commit,
701 source_ref_id, source_scm, target_commit, target_ref_id, target_scm):
716 source_ref_id, source_scm, target_commit, target_ref_id, target_scm,
717 maybe_unreachable=False):
718
702 commit_cache = collections.OrderedDict()
719 commit_cache = collections.OrderedDict()
703 missing_requirements = False
720 missing_requirements = False
721
704 try:
722 try:
705 pre_load = ["author", "date", "message", "branch", "parents"]
723 pre_load = ["author", "date", "message", "branch", "parents"]
706 show_revs = pull_request_at_ver.revisions
724
707 for rev in show_revs:
725 pull_request_commits = pull_request_at_ver.revisions
708 comm = commits_source_repo.get_commit(
726 log.debug('Loading %s commits from %s',
709 commit_id=rev, pre_load=pre_load)
727 len(pull_request_commits), commits_source_repo)
728
729 for rev in pull_request_commits:
730 comm = commits_source_repo.get_commit(commit_id=rev, pre_load=pre_load,
731 maybe_unreachable=maybe_unreachable)
710 commit_cache[comm.raw_id] = comm
732 commit_cache[comm.raw_id] = comm
711
733
712 # Order here matters, we first need to get target, and then
734 # Order here matters, we first need to get target, and then
@@ -715,22 +737,21 b' class RepoPullRequestsView(RepoAppView, '
715 commit_id=safe_str(target_ref_id))
737 commit_id=safe_str(target_ref_id))
716
738
717 source_commit = commits_source_repo.get_commit(
739 source_commit = commits_source_repo.get_commit(
718 commit_id=safe_str(source_ref_id))
740 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
719 except CommitDoesNotExistError:
741 except CommitDoesNotExistError:
720 log.warning(
742 log.warning('Failed to get commit from `{}` repo'.format(
721 'Failed to get commit from `{}` repo'.format(
743 commits_source_repo), exc_info=True)
722 commits_source_repo), exc_info=True)
723 except RepositoryRequirementError:
744 except RepositoryRequirementError:
724 log.warning(
745 log.warning('Failed to get all required data from repo', exc_info=True)
725 'Failed to get all required data from repo', exc_info=True)
726 missing_requirements = True
746 missing_requirements = True
727 ancestor_commit = None
747
748 pr_ancestor_id = pull_request_at_ver.common_ancestor_id
749
728 try:
750 try:
729 ancestor_id = source_scm.get_common_ancestor(
751 ancestor_commit = source_scm.get_commit(pr_ancestor_id)
730 source_commit.raw_id, target_commit.raw_id, target_scm)
731 ancestor_commit = source_scm.get_commit(ancestor_id)
732 except Exception:
752 except Exception:
733 ancestor_commit = None
753 ancestor_commit = None
754
734 return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit
755 return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit
735
756
736 def assure_not_empty_repo(self):
757 def assure_not_empty_repo(self):
@@ -934,6 +955,7 b' class RepoPullRequestsView(RepoAppView, '
934 target_repo = _form['target_repo']
955 target_repo = _form['target_repo']
935 target_ref = _form['target_ref']
956 target_ref = _form['target_ref']
936 commit_ids = _form['revisions'][::-1]
957 commit_ids = _form['revisions'][::-1]
958 common_ancestor_id = _form['common_ancestor']
937
959
938 # find the ancestor for this pr
960 # find the ancestor for this pr
939 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
961 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
@@ -1020,6 +1042,7 b' class RepoPullRequestsView(RepoAppView, '
1020 target_repo=target_repo,
1042 target_repo=target_repo,
1021 target_ref=target_ref,
1043 target_ref=target_ref,
1022 revisions=commit_ids,
1044 revisions=commit_ids,
1045 common_ancestor_id=common_ancestor_id,
1023 reviewers=reviewers,
1046 reviewers=reviewers,
1024 title=pullrequest_title,
1047 title=pullrequest_title,
1025 description=description,
1048 description=description,
@@ -1458,6 +1481,10 b' class RepoPullRequestsView(RepoAppView, '
1458 self.request.matchdict['comment_id'])
1481 self.request.matchdict['comment_id'])
1459 comment_id = comment.comment_id
1482 comment_id = comment.comment_id
1460
1483
1484 if comment.immutable:
1485 # don't allow deleting comments that are immutable
1486 raise HTTPForbidden()
1487
1461 if pull_request.is_closed():
1488 if pull_request.is_closed():
1462 log.debug('comment: forbidden because pull request is closed')
1489 log.debug('comment: forbidden because pull request is closed')
1463 raise HTTPForbidden()
1490 raise HTTPForbidden()
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -54,8 +54,20 b' class RepoReviewRulesView(RepoAppView):'
54 renderer='json_ext')
54 renderer='json_ext')
55 def repo_default_reviewers_data(self):
55 def repo_default_reviewers_data(self):
56 self.load_default_context()
56 self.load_default_context()
57 target_repo_name = self.request.GET.get('target_repo', self.db_repo.repo_name)
57
58 request = self.request
59 source_repo = self.db_repo
60 source_repo_name = source_repo.repo_name
61 target_repo_name = request.GET.get('target_repo', source_repo_name)
58 target_repo = Repository.get_by_repo_name(target_repo_name)
62 target_repo = Repository.get_by_repo_name(target_repo_name)
63
64 source_ref = request.GET['source_ref']
65 target_ref = request.GET['target_ref']
66 source_commit = source_repo.get_commit(source_ref)
67 target_commit = target_repo.get_commit(target_ref)
68
69 current_user = request.user.get_instance()
59 review_data = get_default_reviewers_data(
70 review_data = get_default_reviewers_data(
60 self.db_repo.user, None, None, target_repo, None)
71 current_user, source_repo, source_commit, target_repo, target_commit)
72
61 return review_data
73 return review_data
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,6 +22,7 b' import logging'
22
22
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from packaging.version import Version
25
26
26 from rhodecode import events
27 from rhodecode import events
27 from rhodecode.apps._base import RepoAppView
28 from rhodecode.apps._base import RepoAppView
@@ -64,12 +65,19 b' class RepoSettingsView(RepoAppView):'
64 c = self.load_default_context()
65 c = self.load_default_context()
65 c.active = 'advanced'
66 c.active = 'advanced'
66
67
67 c.default_user_id = User.get_default_user().user_id
68 c.default_user_id = User.get_default_user_id()
68 c.in_public_journal = UserFollowing.query() \
69 c.in_public_journal = UserFollowing.query() \
69 .filter(UserFollowing.user_id == c.default_user_id) \
70 .filter(UserFollowing.user_id == c.default_user_id) \
70 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
71 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
71
72
72 c.ver_info_dict = self.rhodecode_vcs_repo.get_hooks_info()
73 c.ver_info_dict = self.rhodecode_vcs_repo.get_hooks_info()
74 c.hooks_outdated = False
75
76 try:
77 if Version(c.ver_info_dict['pre_version']) < Version(c.rhodecode_version):
78 c.hooks_outdated = True
79 except Exception:
80 pass
73
81
74 # update commit cache if GET flag is present
82 # update commit cache if GET flag is present
75 if self.request.GET.get('update_commit_cache'):
83 if self.request.GET.get('update_commit_cache'):
@@ -212,7 +220,7 b' class RepoSettingsView(RepoAppView):'
212 _ = self.request.translate
220 _ = self.request.translate
213
221
214 try:
222 try:
215 user_id = User.get_default_user().user_id
223 user_id = User.get_default_user_id()
216 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
224 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
217 h.flash(_('Updated repository visibility in public journal'),
225 h.flash(_('Updated repository visibility in public journal'),
218 category='success')
226 category='success')
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -60,11 +60,10 b' def perform_search(request, tmpl_context'
60 errors = e.children
60 errors = e.children
61
61
62 def url_generator(page_num):
62 def url_generator(page_num):
63 q = urllib.quote(safe_str(search_query))
64
63
65 query_params = {
64 query_params = {
66 'page': page_num,
65 'page': page_num,
67 'q': q,
66 'q': safe_str(search_query),
68 'type': safe_str(search_type),
67 'type': safe_str(search_type),
69 'max_lines': search_max_lines,
68 'max_lines': search_max_lines,
70 'sort': search_sort
69 'sort': search_sort
@@ -72,7 +71,6 b' def perform_search(request, tmpl_context'
72
71
73 return '?' + urllib.urlencode(query_params)
72 return '?' + urllib.urlencode(query_params)
74
73
75
76 c = tmpl_context
74 c = tmpl_context
77 search_query = search_params.get('search_query')
75 search_query = search_params.get('search_query')
78 search_type = search_params.get('search_type')
76 search_type = search_params.get('search_type')
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -91,7 +91,6 b' class SshWrapper(object):'
91
91
92 def get_repo_details(self, mode):
92 def get_repo_details(self, mode):
93 vcs_type = mode if mode in ['svn', 'hg', 'git'] else None
93 vcs_type = mode if mode in ['svn', 'hg', 'git'] else None
94 mode = mode
95 repo_name = None
94 repo_name = None
96
95
97 hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$'
96 hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$'
@@ -101,8 +100,7 b' class SshWrapper(object):'
101 repo_name = hg_match.group(1).strip('/')
100 repo_name = hg_match.group(1).strip('/')
102 return vcs_type, repo_name, mode
101 return vcs_type, repo_name, mode
103
102
104 git_pattern = (
103 git_pattern = r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$'
105 r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$')
106 git_match = re.match(git_pattern, self.command)
104 git_match = re.match(git_pattern, self.command)
107 if git_match is not None:
105 if git_match is not None:
108 vcs_type = 'git'
106 vcs_type = 'git'
@@ -115,7 +113,8 b' class SshWrapper(object):'
115
113
116 if svn_match is not None:
114 if svn_match is not None:
117 vcs_type = 'svn'
115 vcs_type = 'svn'
118 # Repo name should be extracted from the input stream
116 # Repo name should be extracted from the input stream, we're unable to
117 # extract it at this point in execution
119 return vcs_type, repo_name, mode
118 return vcs_type, repo_name, mode
120
119
121 return vcs_type, repo_name, mode
120 return vcs_type, repo_name, mode
@@ -188,8 +187,7 b' class SshWrapper(object):'
188 log.debug('SSH Connection info %s', self.get_connection_info())
187 log.debug('SSH Connection info %s', self.get_connection_info())
189
188
190 if shell and self.command is None:
189 if shell and self.command is None:
191 log.info(
190 log.info('Dropping to shell, no command given and shell is allowed')
192 'Dropping to shell, no command given and shell is allowed')
193 os.execl('/bin/bash', '-l')
191 os.execl('/bin/bash', '-l')
194 exit_code = 1
192 exit_code = 1
195
193
@@ -216,8 +214,7 b' class SshWrapper(object):'
216 exit_code = -1
214 exit_code = -1
217
215
218 else:
216 else:
219 log.error(
217 log.error('Unhandled Command: "%s" Aborting.', self.command)
220 'Unhandled Command: "%s" Aborting.', self.command)
221 exit_code = -1
218 exit_code = -1
222
219
223 return exit_code
220 return exit_code
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -66,9 +66,8 b' class VcsServer(object):'
66
66
67 def _check_permissions(self, action):
67 def _check_permissions(self, action):
68 permission = self.user_permissions.get(self.repo_name)
68 permission = self.user_permissions.get(self.repo_name)
69 log.debug(
69 log.debug('permission for %s on %s are: %s',
70 'permission for %s on %s are: %s',
70 self.user, self.repo_name, permission)
71 self.user, self.repo_name, permission)
72
71
73 if not permission:
72 if not permission:
74 log.error('user `%s` permissions to repo:%s are empty. Forbidding access.',
73 log.error('user `%s` permissions to repo:%s are empty. Forbidding access.',
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -68,8 +68,7 b' class GitServer(VcsServer):'
68 self.store = store
68 self.store = store
69 self.ini_path = ini_path
69 self.ini_path = ini_path
70 self.repo_name = repo_name
70 self.repo_name = repo_name
71 self._path = self.git_path = config.get(
71 self._path = self.git_path = config.get('app:main', 'ssh.executable.git')
72 'app:main', 'ssh.executable.git')
73
72
74 self.repo_mode = repo_mode
73 self.repo_mode = repo_mode
75 self.tunnel = GitTunnelWrapper(server=self)
74 self.tunnel = GitTunnelWrapper(server=self)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -67,9 +67,12 b' class SubversionTunnelWrapper(object):'
67
67
68 def command(self):
68 def command(self):
69 root = self.server.get_root_store()
69 root = self.server.get_root_store()
70 username = self.server.user.username
71
70 command = [
72 command = [
71 self.server.svn_path, '-t',
73 self.server.svn_path, '-t',
72 '--config-file', self.svn_conf_path,
74 '--config-file', self.svn_conf_path,
75 '--tunnel-user', username,
73 '-r', root]
76 '-r', root]
74 log.debug("Final CMD: %s", ' '.join(command))
77 log.debug("Final CMD: %s", ' '.join(command))
75 return command
78 return command
@@ -95,9 +98,8 b' class SubversionTunnelWrapper(object):'
95 signal.alarm(self.timeout)
98 signal.alarm(self.timeout)
96 first_response = self._read_first_client_response()
99 first_response = self._read_first_client_response()
97 signal.alarm(0)
100 signal.alarm(0)
98 return (
101 return (self._parse_first_client_response(first_response)
99 self._parse_first_client_response(first_response)
102 if first_response else None)
100 if first_response else None)
101
103
102 def patch_first_client_response(self, response, **kwargs):
104 def patch_first_client_response(self, response, **kwargs):
103 self.create_hooks_env()
105 self.create_hooks_env()
@@ -112,9 +114,8 b' class SubversionTunnelWrapper(object):'
112 self.process.stdin.write(buffer_)
114 self.process.stdin.write(buffer_)
113
115
114 def fail(self, message):
116 def fail(self, message):
115 print(
117 print("( failure ( ( 210005 {message} 0: 0 ) ) )".format(
116 "( failure ( ( 210005 {message} 0: 0 ) ) )".format(
118 message=self._svn_string(message)))
117 message=self._svn_string(message)))
118 self.remove_configs()
119 self.remove_configs()
119 self.process.kill()
120 self.process.kill()
120 return 1
121 return 1
@@ -139,6 +140,7 b' class SubversionTunnelWrapper(object):'
139 brackets_stack.pop()
140 brackets_stack.pop()
140 elif next_byte == " " and not brackets_stack:
141 elif next_byte == " " and not brackets_stack:
141 break
142 break
143
142 return buffer_
144 return buffer_
143
145
144 def _parse_first_client_response(self, buffer_):
146 def _parse_first_client_response(self, buffer_):
@@ -149,8 +151,7 b' class SubversionTunnelWrapper(object):'
149 ( version:number ( cap:word ... ) url:string ? ra-client:string
151 ( version:number ( cap:word ... ) url:string ? ra-client:string
150 ( ? client:string ) )
152 ( ? client:string ) )
151
153
152 Please check https://svn.apache.org/repos/asf/subversion/trunk/
154 Please check https://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_ra_svn/protocol
153 subversion/libsvn_ra_svn/protocol
154 """
155 """
155 version_re = r'(?P<version>\d+)'
156 version_re = r'(?P<version>\d+)'
156 capabilities_re = r'\(\s(?P<capabilities>[\w\d\-\ ]+)\s\)'
157 capabilities_re = r'\(\s(?P<capabilities>[\w\d\-\ ]+)\s\)'
@@ -163,8 +164,35 b' class SubversionTunnelWrapper(object):'
163 version=version_re, capabilities=capabilities_re,
164 version=version_re, capabilities=capabilities_re,
164 url=url_re, ra_client=ra_client_re, client=client_re))
165 url=url_re, ra_client=ra_client_re, client=client_re))
165 matcher = regex.match(buffer_)
166 matcher = regex.match(buffer_)
167
166 return matcher.groupdict() if matcher else None
168 return matcher.groupdict() if matcher else None
167
169
170 def _match_repo_name(self, url):
171 """
172 Given an server url, try to match it against ALL known repository names.
173 This handles a tricky SVN case for SSH and subdir commits.
174 E.g if our repo name is my-svn-repo, a svn commit on file in a subdir would
175 result in the url with this subdir added.
176 """
177 # case 1 direct match, we don't do any "heavy" lookups
178 if url in self.server.user_permissions:
179 return url
180
181 log.debug('Extracting repository name from subdir path %s', url)
182 # case 2 we check all permissions, and match closes possible case...
183 # NOTE(dan): In this case we only know that url has a subdir parts, it's safe
184 # to assume that it will have the repo name as prefix, we ensure the prefix
185 # for similar repositories isn't matched by adding a /
186 # e.g subgroup/repo-name/ and subgroup/repo-name-1/ would work correct.
187 for repo_name in self.server.user_permissions:
188 repo_name_prefix = repo_name + '/'
189 if url.startswith(repo_name_prefix):
190 log.debug('Found prefix %s match, returning proper repository name',
191 repo_name_prefix)
192 return repo_name
193
194 return
195
168 def run(self, extras):
196 def run(self, extras):
169 action = 'pull'
197 action = 'pull'
170 self.create_svn_config()
198 self.create_svn_config()
@@ -175,7 +203,8 b' class SubversionTunnelWrapper(object):'
175 return self.fail("Repository name cannot be extracted")
203 return self.fail("Repository name cannot be extracted")
176
204
177 url_parts = urlparse.urlparse(first_response['url'])
205 url_parts = urlparse.urlparse(first_response['url'])
178 self.server.repo_name = url_parts.path.strip('/')
206
207 self.server.repo_name = self._match_repo_name(url_parts.path.strip('/'))
179
208
180 exit_code = self.server._check_permissions(action)
209 exit_code = self.server._check_permissions(action)
181 if exit_code:
210 if exit_code:
@@ -200,10 +229,10 b' class SubversionServer(VcsServer):'
200 .__init__(user, user_permissions, config, env)
229 .__init__(user, user_permissions, config, env)
201 self.store = store
230 self.store = store
202 self.ini_path = ini_path
231 self.ini_path = ini_path
203 # this is set in .run() from input stream
232 # NOTE(dan): repo_name at this point is empty,
233 # this is set later in .run() based from parsed input stream
204 self.repo_name = repo_name
234 self.repo_name = repo_name
205 self._path = self.svn_path = config.get(
235 self._path = self.svn_path = config.get('app:main', 'ssh.executable.svn')
206 'app:main', 'ssh.executable.svn')
207
236
208 self.tunnel = SubversionTunnelWrapper(server=self)
237 self.tunnel = SubversionTunnelWrapper(server=self)
209
238
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -70,8 +70,10 b' class TestSubversionServer(object):'
70 def test_command(self, svn_server):
70 def test_command(self, svn_server):
71 server = svn_server.create()
71 server = svn_server.create()
72 expected_command = [
72 expected_command = [
73 svn_server.svn_path, '-t', '--config-file',
73 svn_server.svn_path, '-t',
74 server.tunnel.svn_conf_path, '-r', svn_server.root
74 '--config-file', server.tunnel.svn_conf_path,
75 '--tunnel-user', svn_server.user.username,
76 '-r', svn_server.root
75 ]
77 ]
76
78
77 assert expected_command == server.tunnel.command()
79 assert expected_command == server.tunnel.command()
@@ -89,6 +91,86 b' class TestSubversionServer(object):'
89 result = server._check_permissions(action)
91 result = server._check_permissions(action)
90 assert result is code
92 assert result is code
91
93
94 @pytest.mark.parametrize('permissions, access_paths, expected_match', [
95 # not matched repository name
96 ({
97 'test-svn': ''
98 }, ['test-svn-1', 'test-svn-1/subpath'],
99 None),
100
101 # exact match
102 ({
103 'test-svn': ''
104 },
105 ['test-svn'],
106 'test-svn'),
107
108 # subdir commits
109 ({
110 'test-svn': ''
111 },
112 ['test-svn/foo',
113 'test-svn/foo/test-svn',
114 'test-svn/trunk/development.txt',
115 ],
116 'test-svn'),
117
118 # subgroups + similar patterns
119 ({
120 'test-svn': '',
121 'test-svn-1': '',
122 'test-svn-subgroup/test-svn': '',
123
124 },
125 ['test-svn-1',
126 'test-svn-1/foo/test-svn',
127 'test-svn-1/test-svn',
128 ],
129 'test-svn-1'),
130
131 # subgroups + similar patterns
132 ({
133 'test-svn-1': '',
134 'test-svn-10': '',
135 'test-svn-100': '',
136 },
137 ['test-svn-10',
138 'test-svn-10/foo/test-svn',
139 'test-svn-10/test-svn',
140 ],
141 'test-svn-10'),
142
143 # subgroups + similar patterns
144 ({
145 'name': '',
146 'nameContains': '',
147 'nameContainsThis': '',
148 },
149 ['nameContains',
150 'nameContains/This',
151 'nameContains/This/test-svn',
152 ],
153 'nameContains'),
154
155 # subgroups + similar patterns
156 ({
157 'test-svn': '',
158 'test-svn-1': '',
159 'test-svn-subgroup/test-svn': '',
160
161 },
162 ['test-svn-subgroup/test-svn',
163 'test-svn-subgroup/test-svn/foo/test-svn',
164 'test-svn-subgroup/test-svn/trunk/example.txt',
165 ],
166 'test-svn-subgroup/test-svn'),
167 ])
168 def test_repo_extraction_on_subdir(self, svn_server, permissions, access_paths, expected_match):
169 server = svn_server.create(user_permissions=permissions)
170 for path in access_paths:
171 repo_name = server.tunnel._match_repo_name(path)
172 assert repo_name == expected_match
173
92 def test_run_returns_executes_command(self, svn_server):
174 def test_run_returns_executes_command(self, svn_server):
93 server = svn_server.create()
175 server = svn_server.create()
94 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
176 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -37,7 +37,7 b' log = logging.getLogger(__name__)'
37
37
38 def write_mod_dav_svn_config(settings):
38 def write_mod_dav_svn_config(settings):
39 use_ssl = str2bool(settings['force_https'])
39 use_ssl = str2bool(settings['force_https'])
40
40 file_path = settings[config_keys.config_file_path]
41 config = _render_mod_dav_svn_config(
41 config = _render_mod_dav_svn_config(
42 use_ssl=use_ssl,
42 use_ssl=use_ssl,
43 parent_path_root=get_rhodecode_base_path(),
43 parent_path_root=get_rhodecode_base_path(),
@@ -45,7 +45,8 b' def write_mod_dav_svn_config(settings):'
45 location_root=settings[config_keys.location_root],
45 location_root=settings[config_keys.location_root],
46 repo_groups=RepoGroup.get_all_repo_groups(),
46 repo_groups=RepoGroup.get_all_repo_groups(),
47 realm=get_rhodecode_realm(), template=settings[config_keys.template])
47 realm=get_rhodecode_realm(), template=settings[config_keys.template])
48 _write_mod_dav_svn_config(config, settings[config_keys.config_file_path])
48 _write_mod_dav_svn_config(config, file_path)
49 return file_path
49
50
50
51
51 def generate_mod_dav_svn_config(registry):
52 def generate_mod_dav_svn_config(registry):
@@ -56,11 +57,11 b' def generate_mod_dav_svn_config(registry'
56 repositories organized in sub folders.
57 repositories organized in sub folders.
57 """
58 """
58 settings = registry.settings
59 settings = registry.settings
59 write_mod_dav_svn_config(settings)
60 file_path = write_mod_dav_svn_config(settings)
60
61
61 # Trigger an event on mod dav svn configuration change.
62 # Trigger an event on mod dav svn configuration change.
62 trigger(ModDavSvnConfigChange(), registry)
63 trigger(ModDavSvnConfigChange(), registry)
63
64 return file_path
64
65
65 def _render_mod_dav_svn_config(
66 def _render_mod_dav_svn_config(
66 parent_path_root, list_parent_path, location_root, repo_groups, realm,
67 parent_path_root, list_parent_path, location_root, repo_groups, realm,
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -153,25 +153,25 b' class CrowdServer(object):'
153 global msg
153 global msg
154 msg = ""
154 msg = ""
155 try:
155 try:
156 rdoc = self.opener.open(request)
156 ret_doc = self.opener.open(request)
157 msg = "".join(rdoc.readlines())
157 msg = ret_doc.read()
158 if not msg and empty_response_ok:
158 if not msg and empty_response_ok:
159 rval = {}
159 ret_val = {}
160 rval["status"] = True
160 ret_val["status"] = True
161 rval["error"] = "Response body was empty"
161 ret_val["error"] = "Response body was empty"
162 elif not noformat:
162 elif not noformat:
163 rval = json.loads(msg)
163 ret_val = json.loads(msg)
164 rval["status"] = True
164 ret_val["status"] = True
165 else:
165 else:
166 rval = "".join(rdoc.readlines())
166 ret_val = msg
167 except Exception as e:
167 except Exception as e:
168 if not noformat:
168 if not noformat:
169 rval = {"status": False,
169 ret_val = {"status": False,
170 "body": body,
170 "body": body,
171 "error": str(e) + "\n" + msg}
171 "error": "{}\n{}".format(e, msg)}
172 else:
172 else:
173 rval = None
173 ret_val = None
174 return rval
174 return ret_val
175
175
176 def user_auth(self, username, password):
176 def user_auth(self, username, password):
177 """Authenticate a user against crowd. Returns brief information about
177 """Authenticate a user against crowd. Returns brief information about
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -69,6 +69,12 b' class LdapAuthnResource(AuthnPluginResou'
69 class AuthLdap(AuthLdapBase):
69 class AuthLdap(AuthLdapBase):
70 default_tls_cert_dir = '/etc/openldap/cacerts'
70 default_tls_cert_dir = '/etc/openldap/cacerts'
71
71
72 scope_labels = {
73 ldap.SCOPE_BASE: 'SCOPE_BASE',
74 ldap.SCOPE_ONELEVEL: 'SCOPE_ONELEVEL',
75 ldap.SCOPE_SUBTREE: 'SCOPE_SUBTREE',
76 }
77
72 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
78 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
73 tls_kind='PLAIN', tls_reqcert='DEMAND', tls_cert_file=None,
79 tls_kind='PLAIN', tls_reqcert='DEMAND', tls_cert_file=None,
74 tls_cert_dir=None, ldap_version=3,
80 tls_cert_dir=None, ldap_version=3,
@@ -85,7 +91,7 b' class AuthLdap(AuthLdapBase):'
85 self.TLS_KIND = tls_kind
91 self.TLS_KIND = tls_kind
86
92
87 if self.TLS_KIND == 'LDAPS':
93 if self.TLS_KIND == 'LDAPS':
88 port = port or 689
94 port = port or 636
89 self.ldap_server_type += 's'
95 self.ldap_server_type += 's'
90
96
91 OPT_X_TLS_DEMAND = 2
97 OPT_X_TLS_DEMAND = 2
@@ -148,19 +154,27 b' class AuthLdap(AuthLdapBase):'
148 log.debug('simple_bind successful')
154 log.debug('simple_bind successful')
149 return ldap_conn
155 return ldap_conn
150
156
151 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
157 def fetch_attrs_from_simple_bind(self, ldap_conn, dn, username, password):
158 scope = ldap.SCOPE_BASE
159 scope_label = self.scope_labels.get(scope)
160 ldap_filter = '(objectClass=*)'
161
152 try:
162 try:
153 log.debug('Trying simple bind with %r', dn)
163 log.debug('Trying authenticated search bind with dn: %r SCOPE: %s (and filter: %s)',
154 server.simple_bind_s(dn, safe_str(password))
164 dn, scope_label, ldap_filter)
155 _dn, attrs = server.search_ext_s(
165 ldap_conn.simple_bind_s(dn, safe_str(password))
156 dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0]
166 response = ldap_conn.search_ext_s(dn, scope, ldap_filter, attrlist=['*', '+'])
157
167
158 return attrs
168 if not response:
169 log.error('search bind returned empty results: %r', response)
170 return {}
171 else:
172 _dn, attrs = response[0]
173 return attrs
159
174
160 except ldap.INVALID_CREDENTIALS:
175 except ldap.INVALID_CREDENTIALS:
161 log.debug(
176 log.debug("LDAP rejected password for user '%s': %s, org_exc:",
162 "LDAP rejected password for user '%s': %s, org_exc:",
177 username, dn, exc_info=True)
163 username, dn, exc_info=True)
164
178
165 def authenticate_ldap(self, username, password):
179 def authenticate_ldap(self, username, password):
166 """
180 """
@@ -179,35 +193,38 b' class AuthLdap(AuthLdapBase):'
179
193
180 self.validate_password(username, password)
194 self.validate_password(username, password)
181 self.validate_username(username)
195 self.validate_username(username)
196 scope_label = self.scope_labels.get(self.SEARCH_SCOPE)
182
197
183 ldap_conn = None
198 ldap_conn = None
184 try:
199 try:
185 ldap_conn = self._get_ldap_conn()
200 ldap_conn = self._get_ldap_conn()
186 filter_ = '(&%s(%s=%s))' % (
201 filter_ = '(&%s(%s=%s))' % (
187 self.LDAP_FILTER, self.attr_login, username)
202 self.LDAP_FILTER, self.attr_login, username)
188 log.debug("Authenticating %r filter %s", self.BASE_DN, filter_)
203 log.debug("Authenticating %r filter %s and scope: %s",
204 self.BASE_DN, filter_, scope_label)
189
205
190 lobjects = ldap_conn.search_ext_s(
206 ldap_objects = ldap_conn.search_ext_s(
191 self.BASE_DN, self.SEARCH_SCOPE, filter_)
207 self.BASE_DN, self.SEARCH_SCOPE, filter_, attrlist=['*', '+'])
192
208
193 if not lobjects:
209 if not ldap_objects:
194 log.debug("No matching LDAP objects for authentication "
210 log.debug("No matching LDAP objects for authentication "
195 "of UID:'%s' username:(%s)", uid, username)
211 "of UID:'%s' username:(%s)", uid, username)
196 raise ldap.NO_SUCH_OBJECT()
212 raise ldap.NO_SUCH_OBJECT()
197
213
198 log.debug('Found matching ldap object, trying to authenticate')
214 log.debug('Found %s matching ldap object[s], trying to authenticate on each one now...', len(ldap_objects))
199 for (dn, _attrs) in lobjects:
215 for (dn, _attrs) in ldap_objects:
200 if dn is None:
216 if dn is None:
201 continue
217 continue
202
218
203 user_attrs = self.fetch_attrs_from_simple_bind(
219 user_attrs = self.fetch_attrs_from_simple_bind(
204 ldap_conn, dn, username, password)
220 ldap_conn, dn, username, password)
221
205 if user_attrs:
222 if user_attrs:
223 log.debug('Got authenticated user attributes from DN:%s', dn)
206 break
224 break
207 else:
225 else:
208 raise LdapPasswordError(
226 raise LdapPasswordError(
209 'Failed to authenticate user `{}` '
227 'Failed to authenticate user `{}` with given password'.format(username))
210 'with given password'.format(username))
211
228
212 except ldap.NO_SUCH_OBJECT:
229 except ldap.NO_SUCH_OBJECT:
213 log.debug("LDAP says no such user '%s' (%s), org_exc:",
230 log.debug("LDAP says no such user '%s' (%s), org_exc:",
@@ -216,8 +233,7 b' class AuthLdap(AuthLdapBase):'
216 except ldap.SERVER_DOWN:
233 except ldap.SERVER_DOWN:
217 org_exc = traceback.format_exc()
234 org_exc = traceback.format_exc()
218 raise LdapConnectionError(
235 raise LdapConnectionError(
219 "LDAP can't access authentication "
236 "LDAP can't access authentication server, org_exc:%s" % org_exc)
220 "server, org_exc:%s" % org_exc)
221 finally:
237 finally:
222 if ldap_conn:
238 if ldap_conn:
223 log.debug('ldap: connection release')
239 log.debug('ldap: connection release')
@@ -249,7 +265,7 b' class LdapSettingsSchema(AuthnPluginSett'
249 colander.Int(),
265 colander.Int(),
250 default=389,
266 default=389,
251 description=_('Custom port that the LDAP server is listening on. '
267 description=_('Custom port that the LDAP server is listening on. '
252 'Default value is: 389, use 689 for LDAPS (SSL)'),
268 'Default value is: 389, use 636 for LDAPS (SSL)'),
253 preparer=strip_whitespace,
269 preparer=strip_whitespace,
254 title=_('Port'),
270 title=_('Port'),
255 validator=colander.Range(min=0, max=65536),
271 validator=colander.Range(min=0, max=65536),
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -60,10 +60,15 b' def load_pyramid_environment(global_conf'
60
60
61 load_rcextensions(root_path=settings_merged['here'])
61 load_rcextensions(root_path=settings_merged['here'])
62
62
63 # Limit backends to `vcs.backends` from configuration
63 # Limit backends to `vcs.backends` from configuration, and preserve the order
64 for alias in rhodecode.BACKENDS.keys():
64 for alias in rhodecode.BACKENDS.keys():
65 if alias not in settings['vcs.backends']:
65 if alias not in settings['vcs.backends']:
66 del rhodecode.BACKENDS[alias]
66 del rhodecode.BACKENDS[alias]
67
68 _sorted_backend = sorted(rhodecode.BACKENDS.items(),
69 key=lambda item: settings['vcs.backends'].index(item[0]))
70 rhodecode.BACKENDS = rhodecode.OrderedDict(_sorted_backend)
71
67 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
72 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
68
73
69 # initialize vcs client and optionally run the server if enabled
74 # initialize vcs client and optionally run the server if enabled
@@ -76,6 +81,7 b' def load_pyramid_environment(global_conf'
76
81
77 rhodecode.PYRAMID_SETTINGS = settings_merged
82 rhodecode.PYRAMID_SETTINGS = settings_merged
78 rhodecode.CONFIG = settings_merged
83 rhodecode.CONFIG = settings_merged
84 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
79
85
80 if vcs_server_enabled:
86 if vcs_server_enabled:
81 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
87 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -611,6 +611,14 b' def _sanitize_cache_settings(settings):'
611 settings,
611 settings,
612 'exception_tracker.store_path',
612 'exception_tracker.store_path',
613 temp_store, lower=False, default_when_empty=True)
613 temp_store, lower=False, default_when_empty=True)
614 _bool_setting(
615 settings,
616 'exception_tracker.send_email',
617 'false')
618 _string_setting(
619 settings,
620 'exception_tracker.email_prefix',
621 '[RHODECODE ERROR]', lower=False, default_when_empty=True)
614
622
615 # cache_perms
623 # cache_perms
616 _string_setting(
624 _string_setting(
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,6 +25,7 b' from .hooks import ('
25 _create_repo_group_hook,
25 _create_repo_group_hook,
26 _pre_create_user_hook,
26 _pre_create_user_hook,
27 _create_user_hook,
27 _create_user_hook,
28 _comment_commit_repo_hook,
28 _delete_repo_hook,
29 _delete_repo_hook,
29 _delete_user_hook,
30 _delete_user_hook,
30 _pre_push_hook,
31 _pre_push_hook,
@@ -33,6 +34,7 b' from .hooks import ('
33 _pull_hook,
34 _pull_hook,
34 _create_pull_request_hook,
35 _create_pull_request_hook,
35 _review_pull_request_hook,
36 _review_pull_request_hook,
37 _comment_pull_request_hook,
36 _update_pull_request_hook,
38 _update_pull_request_hook,
37 _merge_pull_request_hook,
39 _merge_pull_request_hook,
38 _close_pull_request_hook,
40 _close_pull_request_hook,
@@ -40,6 +42,7 b' from .hooks import ('
40
42
41 # set as module attributes, we use those to call hooks. *do not change this*
43 # set as module attributes, we use those to call hooks. *do not change this*
42 CREATE_REPO_HOOK = _create_repo_hook
44 CREATE_REPO_HOOK = _create_repo_hook
45 COMMENT_COMMIT_REPO_HOOK = _comment_commit_repo_hook
43 CREATE_REPO_GROUP_HOOK = _create_repo_group_hook
46 CREATE_REPO_GROUP_HOOK = _create_repo_group_hook
44 PRE_CREATE_USER_HOOK = _pre_create_user_hook
47 PRE_CREATE_USER_HOOK = _pre_create_user_hook
45 CREATE_USER_HOOK = _create_user_hook
48 CREATE_USER_HOOK = _create_user_hook
@@ -51,6 +54,7 b' PRE_PULL_HOOK = _pre_pull_hook'
51 PULL_HOOK = _pull_hook
54 PULL_HOOK = _pull_hook
52 CREATE_PULL_REQUEST = _create_pull_request_hook
55 CREATE_PULL_REQUEST = _create_pull_request_hook
53 REVIEW_PULL_REQUEST = _review_pull_request_hook
56 REVIEW_PULL_REQUEST = _review_pull_request_hook
57 COMMENT_PULL_REQUEST = _comment_pull_request_hook
54 UPDATE_PULL_REQUEST = _update_pull_request_hook
58 UPDATE_PULL_REQUEST = _update_pull_request_hook
55 MERGE_PULL_REQUEST = _merge_pull_request_hook
59 MERGE_PULL_REQUEST = _merge_pull_request_hook
56 CLOSE_PULL_REQUEST = _close_pull_request_hook
60 CLOSE_PULL_REQUEST = _close_pull_request_hook
@@ -24,14 +24,13 b' def _push_hook(*args, **kwargs):'
24 # returns list of dicts with key-val fetched from extra fields
24 # returns list of dicts with key-val fetched from extra fields
25 repo_extra_fields = extra_fields.run(**kwargs)
25 repo_extra_fields = extra_fields.run(**kwargs)
26
26
27 if repo_extra_fields.get('endpoint_url'):
27 endpoint_url = extra_fields.get_field(repo_extra_fields, key='endpoint_url', default='')
28 field_metadata = repo_extra_fields['endpoint_url']
28
29 endpoint = field_metadata['field_value']
29 if endpoint_url:
30 if endpoint:
30 data = {
31 data = {
31 'project': kwargs['repository'],
32 'project': kwargs['repository'],
32 }
33 }
33 response = http_call.run(url=endpoint_url, params=data)
34 response = http_call.run(url=endpoint, params=data)
34 return HookResponse(0, 'Called endpoint {}, with response {}\n'.format(endpoint_url, response))
35 return HookResponse(0, 'Called endpoint {}, with response {}\n'.format(endpoint, response))
36
35
37 return HookResponse(0, '')
36 return HookResponse(0, '')
@@ -24,14 +24,12 b' def _push_hook(*args, **kwargs):'
24 # returns list of dicts with key-val fetched from extra fields
24 # returns list of dicts with key-val fetched from extra fields
25 repo_extra_fields = extra_fields.run(**kwargs)
25 repo_extra_fields = extra_fields.run(**kwargs)
26
26
27 if repo_extra_fields.get('endpoint_url'):
27 endpoint_url = extra_fields.get_field(repo_extra_fields, key='endpoint_url', default='')
28 field_metadata = repo_extra_fields['endpoint_url']
28 if endpoint_url:
29 endpoint = field_metadata['field_value']
29 data = {
30 if endpoint:
30 'some_key': 'val'
31 data = {
31 }
32 'some_key': 'val'
32 response = http_call.run(url=endpoint_url, json_data=data)
33 }
33 return HookResponse(0, 'Called endpoint {}, with response {}'.format(endpoint_url, response))
34 response = http_call.run(url=endpoint, json_data=data)
35 return HookResponse(0, 'Called endpoint {}, with response {}'.format(endpoint, response))
36
34
37 return HookResponse(0, '')
35 return HookResponse(0, '')
@@ -41,11 +41,13 b' def _pre_push_hook(*args, **kwargs):'
41 repo_extra_fields = extra_fields.run(**kwargs)
41 repo_extra_fields = extra_fields.run(**kwargs)
42
42
43 # optionally use 'extra fields' to control the logic per repo
43 # optionally use 'extra fields' to control the logic per repo
44 validate_author = repo_extra_fields.get('validate_author', {}).get('field_value')
44 validate_author = extra_fields.get_field(
45 repo_extra_fields, key='validate_author', default=False)
45 should_validate = str2bool(validate_author)
46 should_validate = str2bool(validate_author)
46
47
47 # optionally store validation regex into extra fields
48 # optionally store validation regex into extra fields
48 validation_regex = repo_extra_fields.get('validation_regex', {}).get('field_value')
49 validation_regex = extra_fields.get_field(
50 repo_extra_fields, key='validation_regex', default='')
49
51
50 def validate_commit_message(commit_message, message_regex=None):
52 def validate_commit_message(commit_message, message_regex=None):
51 """
53 """
@@ -44,13 +44,16 b' def _pre_push_hook(*args, **kwargs):'
44
44
45 # optionally use 'extra fields' to control the logic per repo
45 # optionally use 'extra fields' to control the logic per repo
46 # e.g store a list of patterns to be forbidden e.g `*.exe, *.dump`
46 # e.g store a list of patterns to be forbidden e.g `*.exe, *.dump`
47 forbid_files = repo_extra_fields.get('forbid_files_glob', {}).get('field_value')
47 forbid_files = extra_fields.get_field(repo_extra_fields, key='forbid_files_glob',
48 convert_type=False, default=[])
48 forbid_files = aslist(forbid_files)
49 forbid_files = aslist(forbid_files)
49
50
50 # forbid_files = ['*'] # example pattern
51 # forbid_files = ['*'] # example pattern
51
52
52 # optionally get bytes limit for a single file, e.g 1024 for 1KB
53 # optionally get bytes limit for a single file, e.g 1024 for 1KB
53 forbid_size_over = repo_extra_fields.get('forbid_size_over', {}).get('field_value')
54 forbid_size_over = extra_fields.get_field(repo_extra_fields, key='forbid_size_over',
55 convert_type=False, default=0)
56
54 forbid_size_over = int(forbid_size_over or 0)
57 forbid_size_over = int(forbid_size_over or 0)
55
58
56 # forbid_size_over = 1024 # example 1024
59 # forbid_size_over = 1024 # example 1024
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2019 RhodeCode GmbH
2 # Copyright (C) 2016-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
@@ -53,3 +53,36 b' def run(*args, **kwargs):'
53 fields[field.field_key] = field.get_dict()
53 fields[field.field_key] = field.get_dict()
54
54
55 return fields
55 return fields
56
57
58 class _Undefined(object):
59 pass
60
61
62 def get_field(extra_fields_data, key, default=_Undefined(), convert_type=True):
63 """
64 field_value = get_field(extra_fields, key='ci_endpoint_url', default='')
65 """
66 from ..utils import str2bool, aslist
67
68 if key not in extra_fields_data:
69 if isinstance(default, _Undefined):
70 raise ValueError('key {} not present in extra_fields'.format(key))
71 return default
72
73 # NOTE(dan): from metadata we get field_label, field_value, field_desc, field_type
74 field_metadata = extra_fields_data[key]
75
76 field_value = field_metadata['field_value']
77
78 # NOTE(dan): empty value, use default
79 if not field_value and not isinstance(default, _Undefined):
80 return default
81
82 if convert_type:
83 # 'str', 'unicode', 'list', 'tuple'
84 _type = field_metadata['field_type']
85 if _type in ['list', 'tuple']:
86 field_value = aslist(field_value)
87
88 return field_value
@@ -1,5 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2019 RhodeCode GmbH
2 # Copyright (C) 2016-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2019 RhodeCode GmbH
2 # Copyright (C) 2016-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2019 RhodeCode GmbH
2 # Copyright (C) 2016-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2019 RhodeCode GmbH
2 # Copyright (C) 2016-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -15,11 +15,12 b''
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18 import logging
19 import logging
19 from .utils import DotDict, HookResponse, has_kwargs
20 from .utils import DotDict, HookResponse, has_kwargs
21
20 log = logging.getLogger('rhodecode.' + __name__)
22 log = logging.getLogger('rhodecode.' + __name__)
21
23
22
23 # Config shortcut to keep, all configuration in one place
24 # Config shortcut to keep, all configuration in one place
24 # Example: api_key = CONFIG.my_config.api_key
25 # Example: api_key = CONFIG.my_config.api_key
25 CONFIG = DotDict(
26 CONFIG = DotDict(
@@ -55,13 +56,40 b' def _create_repo_hook(*args, **kwargs):'
55
56
56
57
57 @has_kwargs({
58 @has_kwargs({
58 'group_name': '',
59 'repo_name': '',
59 'group_parent_id': '',
60 'repo_type': '',
61 'description': '',
62 'private': '',
63 'created_on': '',
64 'enable_downloads': '',
65 'repo_id': '',
66 'user_id': '',
67 'enable_statistics': '',
68 'clone_uri': '',
69 'fork_id': '',
70 'group_id': '',
71 'created_by': '',
72 'repository': '',
73 'comment': '',
74 'commit': ''
75 })
76 def _comment_commit_repo_hook(*args, **kwargs):
77 """
78 POST CREATE REPOSITORY COMMENT ON COMMIT HOOK. This function will be executed after
79 a comment is made on this repository commit.
80
81 """
82 return HookResponse(0, '')
83
84
85 @has_kwargs({
86 'group_name': '',
87 'group_parent_id': '',
60 'group_description': '',
88 'group_description': '',
61 'group_id': '',
89 'group_id': '',
62 'user_id': '',
90 'user_id': '',
63 'created_by': '',
91 'created_by': '',
64 'created_on': '',
92 'created_on': '',
65 'enable_locking': ''
93 'enable_locking': ''
66 })
94 })
67 def _create_repo_group_hook(*args, **kwargs):
95 def _create_repo_group_hook(*args, **kwargs):
@@ -73,13 +101,13 b' def _create_repo_group_hook(*args, **kwa'
73
101
74
102
75 @has_kwargs({
103 @has_kwargs({
76 'username': '',
104 'username': '',
77 'password': '',
105 'password': '',
78 'email': '',
106 'email': '',
79 'firstname': '',
107 'firstname': '',
80 'lastname': '',
108 'lastname': '',
81 'active': '',
109 'active': '',
82 'admin': '',
110 'admin': '',
83 'created_by': '',
111 'created_by': '',
84 })
112 })
85 def _pre_create_user_hook(*args, **kwargs):
113 def _pre_create_user_hook(*args, **kwargs):
@@ -173,7 +201,7 b' def _delete_repo_hook(*args, **kwargs):'
173 'emails': '',
201 'emails': '',
174 'inherit_default_permissions': '',
202 'inherit_default_permissions': '',
175 'deleted_by': '',
203 'deleted_by': '',
176 })
204 })
177 def _delete_user_hook(*args, **kwargs):
205 def _delete_user_hook(*args, **kwargs):
178 """
206 """
179 POST DELETE USER HOOK, this function will be executed after each
207 POST DELETE USER HOOK, this function will be executed after each
@@ -348,6 +376,38 b' def _review_pull_request_hook(*args, **k'
348 'scm': 'type of version control "git", "hg", "svn"',
376 'scm': 'type of version control "git", "hg", "svn"',
349 'username': 'username of actor who triggered this event',
377 'username': 'username of actor who triggered this event',
350 'ip': 'ip address of actor who triggered this hook',
378 'ip': 'ip address of actor who triggered this hook',
379
380 'action': '',
381 'repository': 'repository name',
382 'pull_request_id': '',
383 'url': '',
384 'title': '',
385 'description': '',
386 'status': '',
387 'comment': '',
388 'created_on': '',
389 'updated_on': '',
390 'commit_ids': '',
391 'review_status': '',
392 'mergeable': '',
393 'source': '',
394 'target': '',
395 'author': '',
396 'reviewers': '',
397 })
398 def _comment_pull_request_hook(*args, **kwargs):
399 """
400 This hook will be executed after comment is made on a pull request
401 """
402 return HookResponse(0, '')
403
404
405 @has_kwargs({
406 'server_url': 'url of instance that triggered this hook',
407 'config': 'path to .ini config used',
408 'scm': 'type of version control "git", "hg", "svn"',
409 'username': 'username of actor who triggered this event',
410 'ip': 'ip address of actor who triggered this hook',
351 'action': '',
411 'action': '',
352 'repository': 'repository name',
412 'repository': 'repository name',
353 'pull_request_id': '',
413 'pull_request_id': '',
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,8 +18,10 b''
18
18
19 import logging
19 import logging
20 import os
20 import os
21 import string
21 import functools
22 import functools
22 import collections
23 import collections
24 import urllib
23
25
24 log = logging.getLogger('rhodecode.' + __name__)
26 log = logging.getLogger('rhodecode.' + __name__)
25
27
@@ -186,4 +188,12 b' def aslist(obj, sep=None, strip=True):'
186 elif obj is None:
188 elif obj is None:
187 return []
189 return []
188 else:
190 else:
189 return [obj] No newline at end of file
191 return [obj]
192
193
194 class UrlTemplate(string.Template):
195
196 def safe_substitute(self, **kws):
197 # url encode the kw for usage in url
198 kws = {k: urllib.quote(str(v)) for k, v in kws.items()}
199 return super(UrlTemplate, self).safe_substitute(**kws)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +19,6 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import shlex
23 import platform
22 import platform
24
23
25 from rhodecode.model import init_model
24 from rhodecode.model import init_model
@@ -91,3 +90,12 b' def set_instance_id(config):'
91 prefix = instance_id.lstrip('*')
90 prefix = instance_id.lstrip('*')
92 _platform_id = platform.uname()[1] or 'instance'
91 _platform_id = platform.uname()[1] or 'instance'
93 config['instance_id'] = '%s%s-%s' % (prefix, _platform_id, os.getpid())
92 config['instance_id'] = '%s%s-%s' % (prefix, _platform_id, os.getpid())
93
94
95 def get_default_user_id():
96 from rhodecode.model.db import User, Session
97 user_id = Session()\
98 .query(User.user_id)\
99 .filter(User.username == User.DEFAULT_USER)\
100 .scalar()
101 return user_id
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -53,7 +53,7 b' from rhodecode.events.user import ( # p'
53 )
53 )
54
54
55 from rhodecode.events.repo import ( # pragma: no cover
55 from rhodecode.events.repo import ( # pragma: no cover
56 RepoEvent,
56 RepoEvent, RepoCommitCommentEvent,
57 RepoPreCreateEvent, RepoCreateEvent,
57 RepoPreCreateEvent, RepoCreateEvent,
58 RepoPreDeleteEvent, RepoDeleteEvent,
58 RepoPreDeleteEvent, RepoDeleteEvent,
59 RepoPrePushEvent, RepoPushEvent,
59 RepoPrePushEvent, RepoPushEvent,
@@ -75,4 +75,5 b' from rhodecode.events.pullrequest import'
75 PullRequestReviewEvent,
75 PullRequestReviewEvent,
76 PullRequestMergeEvent,
76 PullRequestMergeEvent,
77 PullRequestCloseEvent,
77 PullRequestCloseEvent,
78 PullRequestCommentEvent,
78 )
79 )
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -44,6 +44,9 b' class RhodecodeEvent(object):'
44 self._request = request
44 self._request = request
45 self.utc_timestamp = datetime.datetime.utcnow()
45 self.utc_timestamp = datetime.datetime.utcnow()
46
46
47 def __repr__(self):
48 return '<%s:(%s)>' % (self.__class__.__name__, self.name)
49
47 def get_request(self):
50 def get_request(self):
48 if self._request:
51 if self._request:
49 return self._request
52 return self._request
@@ -116,3 +119,4 b' class RhodeCodeIntegrationEvent(Rhodecod'
116 """
119 """
117 Special subclass for Integration events
120 Special subclass for Integration events
118 """
121 """
122 description = ''
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -77,6 +77,7 b' class PullRequestCreateEvent(PullRequest'
77 """
77 """
78 name = 'pullrequest-create'
78 name = 'pullrequest-create'
79 display_name = lazy_ugettext('pullrequest created')
79 display_name = lazy_ugettext('pullrequest created')
80 description = lazy_ugettext('Event triggered after pull request was created')
80
81
81
82
82 class PullRequestCloseEvent(PullRequestEvent):
83 class PullRequestCloseEvent(PullRequestEvent):
@@ -86,6 +87,7 b' class PullRequestCloseEvent(PullRequestE'
86 """
87 """
87 name = 'pullrequest-close'
88 name = 'pullrequest-close'
88 display_name = lazy_ugettext('pullrequest closed')
89 display_name = lazy_ugettext('pullrequest closed')
90 description = lazy_ugettext('Event triggered after pull request was closed')
89
91
90
92
91 class PullRequestUpdateEvent(PullRequestEvent):
93 class PullRequestUpdateEvent(PullRequestEvent):
@@ -95,6 +97,7 b' class PullRequestUpdateEvent(PullRequest'
95 """
97 """
96 name = 'pullrequest-update'
98 name = 'pullrequest-update'
97 display_name = lazy_ugettext('pullrequest commits updated')
99 display_name = lazy_ugettext('pullrequest commits updated')
100 description = lazy_ugettext('Event triggered after pull requests was updated')
98
101
99
102
100 class PullRequestReviewEvent(PullRequestEvent):
103 class PullRequestReviewEvent(PullRequestEvent):
@@ -104,6 +107,8 b' class PullRequestReviewEvent(PullRequest'
104 """
107 """
105 name = 'pullrequest-review'
108 name = 'pullrequest-review'
106 display_name = lazy_ugettext('pullrequest review changed')
109 display_name = lazy_ugettext('pullrequest review changed')
110 description = lazy_ugettext('Event triggered after a review status of a '
111 'pull requests has changed to other.')
107
112
108 def __init__(self, pullrequest, status):
113 def __init__(self, pullrequest, status):
109 super(PullRequestReviewEvent, self).__init__(pullrequest)
114 super(PullRequestReviewEvent, self).__init__(pullrequest)
@@ -117,6 +122,8 b' class PullRequestMergeEvent(PullRequestE'
117 """
122 """
118 name = 'pullrequest-merge'
123 name = 'pullrequest-merge'
119 display_name = lazy_ugettext('pullrequest merged')
124 display_name = lazy_ugettext('pullrequest merged')
125 description = lazy_ugettext('Event triggered after a successful merge operation '
126 'was executed on a pull request')
120
127
121
128
122 class PullRequestCommentEvent(PullRequestEvent):
129 class PullRequestCommentEvent(PullRequestEvent):
@@ -126,6 +133,8 b' class PullRequestCommentEvent(PullReques'
126 """
133 """
127 name = 'pullrequest-comment'
134 name = 'pullrequest-comment'
128 display_name = lazy_ugettext('pullrequest commented')
135 display_name = lazy_ugettext('pullrequest commented')
136 description = lazy_ugettext('Event triggered after a comment was made on a code '
137 'in the pull request')
129
138
130 def __init__(self, pullrequest, comment):
139 def __init__(self, pullrequest, comment):
131 super(PullRequestCommentEvent, self).__init__(pullrequest)
140 super(PullRequestCommentEvent, self).__init__(pullrequest)
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -181,6 +181,40 b' class RepoEvent(RhodeCodeIntegrationEven'
181 return data
181 return data
182
182
183
183
184 class RepoCommitCommentEvent(RepoEvent):
185 """
186 An instance of this class is emitted as an :term:`event` after a comment is made
187 on repository commit.
188 """
189
190 name = 'repo-commit-comment'
191 display_name = lazy_ugettext('repository commit comment')
192 description = lazy_ugettext('Event triggered after a comment was made '
193 'on commit inside a repository')
194
195 def __init__(self, repo, commit, comment):
196 super(RepoCommitCommentEvent, self).__init__(repo)
197 self.commit = commit
198 self.comment = comment
199
200 def as_dict(self):
201 data = super(RepoCommitCommentEvent, self).as_dict()
202 data['commit'] = {
203 'commit_id': self.commit.raw_id,
204 'commit_message': self.commit.message,
205 'commit_branch': self.commit.branch,
206 }
207
208 data['comment'] = {
209 'comment_id': self.comment.comment_id,
210 'comment_text': self.comment.text,
211 'comment_type': self.comment.comment_type,
212 'comment_f_path': self.comment.f_path,
213 'comment_line_no': self.comment.line_no,
214 }
215 return data
216
217
184 class RepoPreCreateEvent(RepoEvent):
218 class RepoPreCreateEvent(RepoEvent):
185 """
219 """
186 An instance of this class is emitted as an :term:`event` before a repo is
220 An instance of this class is emitted as an :term:`event` before a repo is
@@ -188,6 +222,7 b' class RepoPreCreateEvent(RepoEvent):'
188 """
222 """
189 name = 'repo-pre-create'
223 name = 'repo-pre-create'
190 display_name = lazy_ugettext('repository pre create')
224 display_name = lazy_ugettext('repository pre create')
225 description = lazy_ugettext('Event triggered before repository is created')
191
226
192
227
193 class RepoCreateEvent(RepoEvent):
228 class RepoCreateEvent(RepoEvent):
@@ -197,6 +232,7 b' class RepoCreateEvent(RepoEvent):'
197 """
232 """
198 name = 'repo-create'
233 name = 'repo-create'
199 display_name = lazy_ugettext('repository created')
234 display_name = lazy_ugettext('repository created')
235 description = lazy_ugettext('Event triggered after repository was created')
200
236
201
237
202 class RepoPreDeleteEvent(RepoEvent):
238 class RepoPreDeleteEvent(RepoEvent):
@@ -206,6 +242,7 b' class RepoPreDeleteEvent(RepoEvent):'
206 """
242 """
207 name = 'repo-pre-delete'
243 name = 'repo-pre-delete'
208 display_name = lazy_ugettext('repository pre delete')
244 display_name = lazy_ugettext('repository pre delete')
245 description = lazy_ugettext('Event triggered before a repository is deleted')
209
246
210
247
211 class RepoDeleteEvent(RepoEvent):
248 class RepoDeleteEvent(RepoEvent):
@@ -215,6 +252,7 b' class RepoDeleteEvent(RepoEvent):'
215 """
252 """
216 name = 'repo-delete'
253 name = 'repo-delete'
217 display_name = lazy_ugettext('repository deleted')
254 display_name = lazy_ugettext('repository deleted')
255 description = lazy_ugettext('Event triggered after repository was deleted')
218
256
219
257
220 class RepoVCSEvent(RepoEvent):
258 class RepoVCSEvent(RepoEvent):
@@ -255,6 +293,7 b' class RepoPrePullEvent(RepoVCSEvent):'
255 """
293 """
256 name = 'repo-pre-pull'
294 name = 'repo-pre-pull'
257 display_name = lazy_ugettext('repository pre pull')
295 display_name = lazy_ugettext('repository pre pull')
296 description = lazy_ugettext('Event triggered before repository code is pulled')
258
297
259
298
260 class RepoPullEvent(RepoVCSEvent):
299 class RepoPullEvent(RepoVCSEvent):
@@ -264,6 +303,7 b' class RepoPullEvent(RepoVCSEvent):'
264 """
303 """
265 name = 'repo-pull'
304 name = 'repo-pull'
266 display_name = lazy_ugettext('repository pull')
305 display_name = lazy_ugettext('repository pull')
306 description = lazy_ugettext('Event triggered after repository code was pulled')
267
307
268
308
269 class RepoPrePushEvent(RepoVCSEvent):
309 class RepoPrePushEvent(RepoVCSEvent):
@@ -273,6 +313,8 b' class RepoPrePushEvent(RepoVCSEvent):'
273 """
313 """
274 name = 'repo-pre-push'
314 name = 'repo-pre-push'
275 display_name = lazy_ugettext('repository pre push')
315 display_name = lazy_ugettext('repository pre push')
316 description = lazy_ugettext('Event triggered before the code is '
317 'pushed to a repository')
276
318
277
319
278 class RepoPushEvent(RepoVCSEvent):
320 class RepoPushEvent(RepoVCSEvent):
@@ -284,6 +326,8 b' class RepoPushEvent(RepoVCSEvent):'
284 """
326 """
285 name = 'repo-push'
327 name = 'repo-push'
286 display_name = lazy_ugettext('repository push')
328 display_name = lazy_ugettext('repository push')
329 description = lazy_ugettext('Event triggered after the code was '
330 'pushed to a repository')
287
331
288 def __init__(self, repo_name, pushed_commit_ids, extras):
332 def __init__(self, repo_name, pushed_commit_ids, extras):
289 super(RepoPushEvent, self).__init__(repo_name, extras)
333 super(RepoPushEvent, self).__init__(repo_name, extras)
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -60,6 +60,7 b' class RepoGroupCreateEvent(RepoGroupEven'
60 """
60 """
61 name = 'repo-group-create'
61 name = 'repo-group-create'
62 display_name = lazy_ugettext('repository group created')
62 display_name = lazy_ugettext('repository group created')
63 description = lazy_ugettext('Event triggered after a repository group was created')
63
64
64
65
65 class RepoGroupDeleteEvent(RepoGroupEvent):
66 class RepoGroupDeleteEvent(RepoGroupEvent):
@@ -69,6 +70,7 b' class RepoGroupDeleteEvent(RepoGroupEven'
69 """
70 """
70 name = 'repo-group-delete'
71 name = 'repo-group-delete'
71 display_name = lazy_ugettext('repository group deleted')
72 display_name = lazy_ugettext('repository group deleted')
73 description = lazy_ugettext('Event triggered after a repository group was deleted')
72
74
73
75
74 class RepoGroupUpdateEvent(RepoGroupEvent):
76 class RepoGroupUpdateEvent(RepoGroupEvent):
@@ -78,3 +80,4 b' class RepoGroupUpdateEvent(RepoGroupEven'
78 """
80 """
79 name = 'repo-group-update'
81 name = 'repo-group-update'
80 display_name = lazy_ugettext('repository group update')
82 display_name = lazy_ugettext('repository group update')
83 description = lazy_ugettext('Event triggered after a repository group was updated')
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012-2019 RhodeCode GmbH
2 # Copyright (C) 2012-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -125,6 +125,19 b' class IntegrationTypeBase(object):'
125 """
125 """
126 return colander.Schema()
126 return colander.Schema()
127
127
128 def event_enabled(self, event):
129 """
130 Checks if submitted event is enabled based on the plugin settings
131 :param event:
132 :return: bool
133 """
134 allowed_events = self.settings['events']
135 if event.name not in allowed_events:
136 log.debug('event ignored: %r event %s not in allowed set of events %s',
137 event, event.name, allowed_events)
138 return False
139 return True
140
128
141
129 class EEIntegration(IntegrationTypeBase):
142 class EEIntegration(IntegrationTypeBase):
130 description = 'Integration available in RhodeCode EE edition.'
143 description = 'Integration available in RhodeCode EE edition.'
@@ -139,30 +152,57 b' class EEIntegration(IntegrationTypeBase)'
139 # Helpers #
152 # Helpers #
140 # updating this required to update the `common_vars` as well.
153 # updating this required to update the `common_vars` as well.
141 WEBHOOK_URL_VARS = [
154 WEBHOOK_URL_VARS = [
142 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
155 # GENERAL
143 ('repo_name', 'Full name of the repository'),
156 ('General', [
144 ('repo_type', 'VCS type of repository'),
157 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
145 ('repo_id', 'Unique id of repository'),
158 ('repo_name', 'Full name of the repository'),
146 ('repo_url', 'Repository url'),
159 ('repo_type', 'VCS type of repository'),
160 ('repo_id', 'Unique id of repository'),
161 ('repo_url', 'Repository url'),
162 ]
163 ),
147 # extra repo fields
164 # extra repo fields
148 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
165 ('Repository', [
149
166 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
167 ]
168 ),
150 # special attrs below that we handle, using multi-call
169 # special attrs below that we handle, using multi-call
151 ('branch', 'Name of each branch submitted, if any.'),
170 ('Commit push - Multicalls', [
152 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
171 ('branch', 'Name of each branch submitted, if any.'),
153 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
172 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
154
173 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
174 ]
175 ),
155 # pr events vars
176 # pr events vars
156 ('pull_request_id', 'Unique ID of the pull request.'),
177 ('Pull request', [
157 ('pull_request_title', 'Title of the pull request.'),
178 ('pull_request_id', 'Unique ID of the pull request.'),
158 ('pull_request_url', 'Pull request url.'),
179 ('pull_request_title', 'Title of the pull request.'),
159 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
180 ('pull_request_url', 'Pull request url.'),
160 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
181 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
161 'Changes after PR update'),
182 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
183 'Changes after PR update'),
184 ]
185 ),
186 # commit comment event vars
187 ('Commit comment', [
188 ('commit_comment_id', 'Unique ID of the comment made on a commit.'),
189 ('commit_comment_text', 'Text of commit comment.'),
190 ('commit_comment_type', 'Type of comment, e.g note/todo.'),
162
191
192 ('commit_comment_f_path', 'Optionally path of file for inline comments.'),
193 ('commit_comment_line_no', 'Line number of the file: eg o10, or n200'),
194
195 ('commit_comment_commit_id', 'Commit id that comment was left at.'),
196 ('commit_comment_commit_branch', 'Commit branch that comment was left at'),
197 ('commit_comment_commit_message', 'Commit message that comment was left at'),
198 ]
199 ),
163 # user who triggers the call
200 # user who triggers the call
164 ('username', 'User who triggered the call.'),
201 ('Caller', [
165 ('user_id', 'User id who triggered the call.'),
202 ('username', 'User who triggered the call.'),
203 ('user_id', 'User id who triggered the call.'),
204 ]
205 ),
166 ]
206 ]
167
207
168 # common vars for url template used for CI plugins. Shared with webhook
208 # common vars for url template used for CI plugins. Shared with webhook
@@ -271,6 +311,26 b' class WebhookDataHandler(CommitParsingDa'
271
311
272 return url_calls
312 return url_calls
273
313
314 def repo_commit_comment_handler(self, event, data):
315 url = self.get_base_parsed_template(data)
316 log.debug('register %s call(%s) to url %s', self.name, event, url)
317 comment_vars = [
318 ('commit_comment_id', data['comment']['comment_id']),
319 ('commit_comment_text', data['comment']['comment_text']),
320 ('commit_comment_type', data['comment']['comment_type']),
321
322 ('commit_comment_f_path', data['comment']['comment_f_path']),
323 ('commit_comment_line_no', data['comment']['comment_line_no']),
324
325 ('commit_comment_commit_id', data['commit']['commit_id']),
326 ('commit_comment_commit_branch', data['commit']['commit_branch']),
327 ('commit_comment_commit_message', data['commit']['commit_message']),
328 ]
329 for k, v in comment_vars:
330 url = UrlTmpl(url).safe_substitute(**{k: v})
331
332 return [(url, self.headers, data)]
333
274 def repo_create_event_handler(self, event, data):
334 def repo_create_event_handler(self, event, data):
275 url = self.get_base_parsed_template(data)
335 url = self.get_base_parsed_template(data)
276 log.debug('register %s call(%s) to url %s', self.name, event, url)
336 log.debug('register %s call(%s) to url %s', self.name, event, url)
@@ -298,12 +358,13 b' class WebhookDataHandler(CommitParsingDa'
298 return self.repo_push_event_handler(event, data)
358 return self.repo_push_event_handler(event, data)
299 elif isinstance(event, events.RepoCreateEvent):
359 elif isinstance(event, events.RepoCreateEvent):
300 return self.repo_create_event_handler(event, data)
360 return self.repo_create_event_handler(event, data)
361 elif isinstance(event, events.RepoCommitCommentEvent):
362 return self.repo_commit_comment_handler(event, data)
301 elif isinstance(event, events.PullRequestEvent):
363 elif isinstance(event, events.PullRequestEvent):
302 return self.pull_request_event_handler(event, data)
364 return self.pull_request_event_handler(event, data)
303 else:
365 else:
304 raise ValueError(
366 raise ValueError(
305 'event type `%s` not in supported list: %s' % (
367 'event type `{}` has no handler defined'.format(event.__class__))
306 event.__class__, events))
307
368
308
369
309 def get_auth(settings):
370 def get_auth(settings):
@@ -320,9 +381,13 b' def get_web_token(settings):'
320
381
321
382
322 def get_url_vars(url_vars):
383 def get_url_vars(url_vars):
323 return '\n'.join(
384 items = []
324 '{} - {}'.format('${' + key + '}', explanation)
385
325 for key, explanation in url_vars)
386 for section, section_items in url_vars:
387 items.append('\n*{}*'.format(section))
388 for key, explanation in section_items:
389 items.append(' {} - {}'.format('${' + key + '}', explanation))
390 return '\n'.join(items)
326
391
327
392
328 def render_with_traceback(template, *args, **kwargs):
393 def render_with_traceback(template, *args, **kwargs):
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,13 +19,14 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22 import deform
23 import logging
22 import logging
23
24 import colander
24 import colander
25
25 import deform.widget
26 from mako.template import Template
26 from mako.template import Template
27
27
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
29 from rhodecode.translation import _
30 from rhodecode.translation import _
30 from rhodecode.lib.celerylib import run_task
31 from rhodecode.lib.celerylib import run_task
31 from rhodecode.lib.celerylib import tasks
32 from rhodecode.lib.celerylib import tasks
@@ -174,6 +175,10 b' class EmailIntegrationType(IntegrationTy'
174 display_name = _('Email')
175 display_name = _('Email')
175 description = _('Send repo push summaries to a list of recipients via email')
176 description = _('Send repo push summaries to a list of recipients via email')
176
177
178 valid_events = [
179 events.RepoPushEvent
180 ]
181
177 @classmethod
182 @classmethod
178 def icon(cls):
183 def icon(cls):
179 return '''
184 return '''
@@ -240,59 +245,86 b' class EmailIntegrationType(IntegrationTy'
240
245
241 def settings_schema(self):
246 def settings_schema(self):
242 schema = EmailSettingsSchema()
247 schema = EmailSettingsSchema()
248 schema.add(colander.SchemaNode(
249 colander.Set(),
250 widget=CheckboxChoiceWidgetDesc(
251 values=sorted(
252 [(e.name, e.display_name, e.description) for e in self.valid_events]
253 ),
254 ),
255 description="List of events activated for this integration",
256 name='events'
257 ))
243 return schema
258 return schema
244
259
245 def send_event(self, event):
260 def send_event(self, event):
246 data = event.as_dict()
261 log.debug('handling event %s with integration %s', event.name, self)
247 log.debug('got event: %r', event)
262
263 if event.__class__ not in self.valid_events:
264 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
265 return
266
267 if not self.event_enabled(event):
268 # NOTE(marcink): for legacy reasons we're skipping this check...
269 # since the email event haven't had any settings...
270 pass
248
271
272 handler = EmailEventHandler(self.settings)
273 handler(event, event_data=event.as_dict())
274
275
276 class EmailEventHandler(object):
277 def __init__(self, integration_settings):
278 self.integration_settings = integration_settings
279
280 def __call__(self, event, event_data):
249 if isinstance(event, events.RepoPushEvent):
281 if isinstance(event, events.RepoPushEvent):
250 repo_push_handler(data, self.settings)
282 self.repo_push_handler(event, event_data)
251 else:
283 else:
252 log.debug('ignoring event: %r', event)
284 log.debug('ignoring event: %r', event)
253
285
254
286 def repo_push_handler(self, event, data):
255 def repo_push_handler(data, settings):
287 commit_num = len(data['push']['commits'])
256 commit_num = len(data['push']['commits'])
288 server_url = data['server_url']
257 server_url = data['server_url']
258
289
259 if commit_num == 1:
290 if commit_num == 1:
260 if data['push']['branches']:
291 if data['push']['branches']:
261 _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
292 _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
262 else:
293 else:
263 _subject = '[{repo_name}] {author} pushed {commit_num} commit'
294 _subject = '[{repo_name}] {author} pushed {commit_num} commit'
264 subject = _subject.format(
295 subject = _subject.format(
265 author=data['actor']['username'],
296 author=data['actor']['username'],
266 repo_name=data['repo']['repo_name'],
297 repo_name=data['repo']['repo_name'],
267 commit_num=commit_num,
298 commit_num=commit_num,
268 branches=', '.join(
299 branches=', '.join(
269 branch['name'] for branch in data['push']['branches'])
300 branch['name'] for branch in data['push']['branches'])
270 )
301 )
271 else:
272 if data['push']['branches']:
273 _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
274 else:
302 else:
275 _subject = '[{repo_name}] {author} pushed {commit_num} commits'
303 if data['push']['branches']:
276 subject = _subject.format(
304 _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
277 author=data['actor']['username'],
305 else:
278 repo_name=data['repo']['repo_name'],
306 _subject = '[{repo_name}] {author} pushed {commit_num} commits'
279 commit_num=commit_num,
307 subject = _subject.format(
280 branches=', '.join(
308 author=data['actor']['username'],
281 branch['name'] for branch in data['push']['branches']))
309 repo_name=data['repo']['repo_name'],
310 commit_num=commit_num,
311 branches=', '.join(
312 branch['name'] for branch in data['push']['branches']))
282
313
283 email_body_plaintext = render_with_traceback(
314 email_body_plaintext = render_with_traceback(
284 REPO_PUSH_TEMPLATE_PLAINTEXT,
315 REPO_PUSH_TEMPLATE_PLAINTEXT,
285 data=data,
316 data=data,
286 subject=subject,
317 subject=subject,
287 instance_url=server_url)
318 instance_url=server_url)
288
319
289 email_body_html = render_with_traceback(
320 email_body_html = render_with_traceback(
290 REPO_PUSH_TEMPLATE_HTML,
321 REPO_PUSH_TEMPLATE_HTML,
291 data=data,
322 data=data,
292 subject=subject,
323 subject=subject,
293 instance_url=server_url)
324 instance_url=server_url)
294
325
295 for email_address in settings['recipients']:
326 recipients = self.integration_settings['recipients']
296 run_task(
327 for email_address in recipients:
297 tasks.send_email, email_address, subject,
328 run_task(
298 email_body_plaintext, email_body_html)
329 tasks.send_email, email_address, subject,
330 email_body_plaintext, email_body_html)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,6 +26,7 b' import colander'
26 import textwrap
26 import textwrap
27 from mako.template import Template
27 from mako.template import Template
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
29 from rhodecode.translation import _
30 from rhodecode.translation import _
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
32 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
@@ -119,13 +120,10 b' class HipchatIntegrationType(Integration'
119
120
120 def send_event(self, event):
121 def send_event(self, event):
121 if event.__class__ not in self.valid_events:
122 if event.__class__ not in self.valid_events:
122 log.debug('event not valid: %r', event)
123 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
123 return
124 return
124
125
125 allowed_events = self.settings['events']
126 if not self.event_enabled(event):
126 if event.name not in allowed_events:
127 log.debug('event ignored: %r event %s not in allowed events %s',
128 event, event.name, allowed_events)
129 return
127 return
130
128
131 data = event.as_dict()
129 data = event.as_dict()
@@ -133,8 +131,6 b' class HipchatIntegrationType(Integration'
133 text = '<b>%s<b> caused a <b>%s</b> event' % (
131 text = '<b>%s<b> caused a <b>%s</b> event' % (
134 data['actor']['username'], event.name)
132 data['actor']['username'], event.name)
135
133
136 log.debug('handling hipchat event for %s', event.name)
137
138 if isinstance(event, events.PullRequestCommentEvent):
134 if isinstance(event, events.PullRequestCommentEvent):
139 text = self.format_pull_request_comment_event(event, data)
135 text = self.format_pull_request_comment_event(event, data)
140 elif isinstance(event, events.PullRequestReviewEvent):
136 elif isinstance(event, events.PullRequestReviewEvent):
@@ -154,12 +150,12 b' class HipchatIntegrationType(Integration'
154 schema = HipchatSettingsSchema()
150 schema = HipchatSettingsSchema()
155 schema.add(colander.SchemaNode(
151 schema.add(colander.SchemaNode(
156 colander.Set(),
152 colander.Set(),
157 widget=deform.widget.CheckboxChoiceWidget(
153 widget=CheckboxChoiceWidgetDesc(
158 values=sorted(
154 values=sorted(
159 [(e.name, e.display_name) for e in self.valid_events]
155 [(e.name, e.display_name, e.description) for e in self.valid_events]
160 )
156 ),
161 ),
157 ),
162 description="Events activated for this integration",
158 description="List of events activated for this integration",
163 name='events'
159 name='events'
164 ))
160 ))
165
161
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -30,6 +30,7 b' import colander'
30 from mako.template import Template
30 from mako.template import Template
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
33 from rhodecode.translation import _
34 from rhodecode.translation import _
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
36 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
@@ -134,14 +135,13 b' class SlackIntegrationType(IntegrationTy'
134 ]
135 ]
135
136
136 def send_event(self, event):
137 def send_event(self, event):
138 log.debug('handling event %s with integration %s', event.name, self)
139
137 if event.__class__ not in self.valid_events:
140 if event.__class__ not in self.valid_events:
138 log.debug('event not valid: %r', event)
141 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
139 return
142 return
140
143
141 allowed_events = self.settings['events']
144 if not self.event_enabled(event):
142 if event.name not in allowed_events:
143 log.debug('event ignored: %r event %s not in allowed events %s',
144 event, event.name, allowed_events)
145 return
145 return
146
146
147 data = event.as_dict()
147 data = event.as_dict()
@@ -154,8 +154,6 b' class SlackIntegrationType(IntegrationTy'
154 fields = None
154 fields = None
155 overrides = None
155 overrides = None
156
156
157 log.debug('handling slack event for %s', event.name)
158
159 if isinstance(event, events.PullRequestCommentEvent):
157 if isinstance(event, events.PullRequestCommentEvent):
160 (title, text, fields, overrides) \
158 (title, text, fields, overrides) \
161 = self.format_pull_request_comment_event(event, data)
159 = self.format_pull_request_comment_event(event, data)
@@ -176,12 +174,12 b' class SlackIntegrationType(IntegrationTy'
176 schema = SlackSettingsSchema()
174 schema = SlackSettingsSchema()
177 schema.add(colander.SchemaNode(
175 schema.add(colander.SchemaNode(
178 colander.Set(),
176 colander.Set(),
179 widget=deform.widget.CheckboxChoiceWidget(
177 widget=CheckboxChoiceWidgetDesc(
180 values=sorted(
178 values=sorted(
181 [(e.name, e.display_name) for e in self.valid_events]
179 [(e.name, e.display_name, e.description) for e in self.valid_events]
182 )
180 ),
183 ),
181 ),
184 description="Events activated for this integration",
182 description="List of events activated for this integration",
185 name='events'
183 name='events'
186 ))
184 ))
187
185
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,13 +20,14 b''
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22
22
23 import deform
24 import deform.widget
23 import deform.widget
25 import logging
24 import logging
26 import colander
25 import colander
27
26
28 import rhodecode
27 import rhodecode
29 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.lib.colander_utils import strip_whitespace
30 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
30 from rhodecode.translation import _
31 from rhodecode.translation import _
31 from rhodecode.integrations.types.base import (
32 from rhodecode.integrations.types.base import (
32 IntegrationTypeBase, get_auth, get_web_token, get_url_vars,
33 IntegrationTypeBase, get_auth, get_web_token, get_url_vars,
@@ -53,11 +54,12 b' class WebhookSettingsSchema(colander.Sch'
53 'objects in data in such cases.'),
54 'objects in data in such cases.'),
54 missing=colander.required,
55 missing=colander.required,
55 required=True,
56 required=True,
57 preparer=strip_whitespace,
56 validator=colander.url,
58 validator=colander.url,
57 widget=widgets.CodeMirrorWidget(
59 widget=widgets.CodeMirrorWidget(
58 help_block_collapsable_name='Show url variables',
60 help_block_collapsable_name='Show url variables',
59 help_block_collapsable=(
61 help_block_collapsable=(
60 'E.g http://my-serv/trigger_job/${{event_name}}'
62 'E.g http://my-serv.com/trigger_job/${{event_name}}'
61 '?PR_ID=${{pull_request_id}}'
63 '?PR_ID=${{pull_request_id}}'
62 '\nFull list of vars:\n{}'.format(URL_VARS)),
64 '\nFull list of vars:\n{}'.format(URL_VARS)),
63 codemirror_mode='text',
65 codemirror_mode='text',
@@ -146,34 +148,31 b' class WebhookIntegrationType(Integration'
146 events.PullRequestCreateEvent,
148 events.PullRequestCreateEvent,
147 events.RepoPushEvent,
149 events.RepoPushEvent,
148 events.RepoCreateEvent,
150 events.RepoCreateEvent,
151 events.RepoCommitCommentEvent,
149 ]
152 ]
150
153
151 def settings_schema(self):
154 def settings_schema(self):
152 schema = WebhookSettingsSchema()
155 schema = WebhookSettingsSchema()
153 schema.add(colander.SchemaNode(
156 schema.add(colander.SchemaNode(
154 colander.Set(),
157 colander.Set(),
155 widget=deform.widget.CheckboxChoiceWidget(
158 widget=CheckboxChoiceWidgetDesc(
156 values=sorted(
159 values=sorted(
157 [(e.name, e.display_name) for e in self.valid_events]
160 [(e.name, e.display_name, e.description) for e in self.valid_events]
158 )
161 ),
159 ),
162 ),
160 description="Events activated for this integration",
163 description="List of events activated for this integration",
161 name='events'
164 name='events'
162 ))
165 ))
163 return schema
166 return schema
164
167
165 def send_event(self, event):
168 def send_event(self, event):
166 log.debug(
169 log.debug('handling event %s with integration %s', event.name, self)
167 'handling event %s with Webhook integration %s', event.name, self)
168
170
169 if event.__class__ not in self.valid_events:
171 if event.__class__ not in self.valid_events:
170 log.debug('event not valid: %r', event)
172 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
171 return
173 return
172
174
173 allowed_events = self.settings['events']
175 if not self.event_enabled(event):
174 if event.name not in allowed_events:
175 log.debug('event ignored: %r event %s not in allowed events %s',
176 event, event.name, allowed_events)
177 return
176 return
178
177
179 data = event.as_dict()
178 data = event.as_dict()
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -23,6 +23,8 b' authentication and permission libraries'
23 """
23 """
24
24
25 import os
25 import os
26
27 import colander
26 import time
28 import time
27 import collections
29 import collections
28 import fnmatch
30 import fnmatch
@@ -45,15 +47,14 b' from rhodecode.model import meta'
45 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
46 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
47 from rhodecode.model.db import (
49 from rhodecode.model.db import (
48 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
50 false, User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
49 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
51 UserIpMap, UserApiKeys, RepoGroup, UserGroup, UserNotice)
50 from rhodecode.lib import rc_cache
52 from rhodecode.lib import rc_cache
51 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
53 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
52 from rhodecode.lib.utils import (
54 from rhodecode.lib.utils import (
53 get_repo_slug, get_repo_group_slug, get_user_group_slug)
55 get_repo_slug, get_repo_group_slug, get_user_group_slug)
54 from rhodecode.lib.caching_query import FromCache
56 from rhodecode.lib.caching_query import FromCache
55
57
56
57 if rhodecode.is_unix:
58 if rhodecode.is_unix:
58 import bcrypt
59 import bcrypt
59
60
@@ -1312,7 +1313,10 b' class AuthUser(object):'
1312 if not perms:
1313 if not perms:
1313 perms = AuthUser.repo_read_perms
1314 perms = AuthUser.repo_read_perms
1314
1315
1315 def _cached_repo_acl(user_id, perm_def, _name_filter):
1316 if not isinstance(perms, list):
1317 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1318
1319 def _cached_repo_acl(perm_def, _name_filter):
1316 qry = Repository.query()
1320 qry = Repository.query()
1317 if _name_filter:
1321 if _name_filter:
1318 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1322 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
@@ -1322,7 +1326,21 b' class AuthUser(object):'
1322 return [x.repo_id for x in
1326 return [x.repo_id for x in
1323 RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1327 RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1324
1328
1325 return _cached_repo_acl(self.user_id, perms, name_filter)
1329 log.debug('Computing REPO ACL IDS user %s', self)
1330
1331 cache_namespace_uid = 'cache_user_repo_acl_ids.{}'.format(self.user_id)
1332 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1333
1334 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1335 def compute_repo_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1336 return _cached_repo_acl(perm_def, _name_filter)
1337
1338 start = time.time()
1339 result = compute_repo_acl_ids('v1', self.user_id, perms, name_filter)
1340 total = time.time() - start
1341 log.debug('REPO ACL IDS for user %s computed in %.4fs', self, total)
1342
1343 return result
1326
1344
1327 def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1345 def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1328 if not perms:
1346 if not perms:
@@ -1346,7 +1364,10 b' class AuthUser(object):'
1346 if not perms:
1364 if not perms:
1347 perms = AuthUser.repo_group_read_perms
1365 perms = AuthUser.repo_group_read_perms
1348
1366
1349 def _cached_repo_group_acl(user_id, perm_def, _name_filter):
1367 if not isinstance(perms, list):
1368 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1369
1370 def _cached_repo_group_acl(perm_def, _name_filter):
1350 qry = RepoGroup.query()
1371 qry = RepoGroup.query()
1351 if _name_filter:
1372 if _name_filter:
1352 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1373 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
@@ -1356,7 +1377,21 b' class AuthUser(object):'
1356 return [x.group_id for x in
1377 return [x.group_id for x in
1357 RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1378 RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1358
1379
1359 return _cached_repo_group_acl(self.user_id, perms, name_filter)
1380 log.debug('Computing REPO GROUP ACL IDS user %s', self)
1381
1382 cache_namespace_uid = 'cache_user_repo_group_acl_ids.{}'.format(self.user_id)
1383 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1384
1385 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1386 def compute_repo_group_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1387 return _cached_repo_group_acl(perm_def, _name_filter)
1388
1389 start = time.time()
1390 result = compute_repo_group_acl_ids('v1', self.user_id, perms, name_filter)
1391 total = time.time() - start
1392 log.debug('REPO GROUP ACL IDS for user %s computed in %.4fs', self, total)
1393
1394 return result
1360
1395
1361 def user_group_acl_ids_from_stack(self, perms=None, cache=False):
1396 def user_group_acl_ids_from_stack(self, perms=None, cache=False):
1362 if not perms:
1397 if not perms:
@@ -1378,17 +1413,34 b' class AuthUser(object):'
1378 if not perms:
1413 if not perms:
1379 perms = AuthUser.user_group_read_perms
1414 perms = AuthUser.user_group_read_perms
1380
1415
1381 def _cached_user_group_acl(user_id, perm_def, name_filter):
1416 if not isinstance(perms, list):
1417 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1418
1419 def _cached_user_group_acl(perm_def, _name_filter):
1382 qry = UserGroup.query()
1420 qry = UserGroup.query()
1383 if name_filter:
1421 if _name_filter:
1384 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1422 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1385 qry = qry.filter(
1423 qry = qry.filter(
1386 UserGroup.users_group_name.ilike(ilike_expression))
1424 UserGroup.users_group_name.ilike(ilike_expression))
1387
1425
1388 return [x.users_group_id for x in
1426 return [x.users_group_id for x in
1389 UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1427 UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1390
1428
1391 return _cached_user_group_acl(self.user_id, perms, name_filter)
1429 log.debug('Computing USER GROUP ACL IDS user %s', self)
1430
1431 cache_namespace_uid = 'cache_user_user_group_acl_ids.{}'.format(self.user_id)
1432 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1433
1434 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1435 def compute_user_group_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1436 return _cached_user_group_acl(perm_def, _name_filter)
1437
1438 start = time.time()
1439 result = compute_user_group_acl_ids('v1', self.user_id, perms, name_filter)
1440 total = time.time() - start
1441 log.debug('USER GROUP ACL IDS for user %s computed in %.4fs', self, total)
1442
1443 return result
1392
1444
1393 @property
1445 @property
1394 def ip_allowed(self):
1446 def ip_allowed(self):
@@ -1402,6 +1454,7 b' class AuthUser(object):'
1402 inherit = self.inherit_default_permissions
1454 inherit = self.inherit_default_permissions
1403 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1455 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1404 inherit_from_default=inherit)
1456 inherit_from_default=inherit)
1457
1405 @property
1458 @property
1406 def personal_repo_group(self):
1459 def personal_repo_group(self):
1407 return RepoGroup.get_user_personal_repo_group(self.user_id)
1460 return RepoGroup.get_user_personal_repo_group(self.user_id)
@@ -1455,9 +1508,40 b' class AuthUser(object):'
1455
1508
1456 return rule, default_perm
1509 return rule, default_perm
1457
1510
1511 def get_notice_messages(self):
1512
1513 notice_level = 'notice-error'
1514 notice_messages = []
1515 if self.is_default:
1516 return [], notice_level
1517
1518 notices = UserNotice.query()\
1519 .filter(UserNotice.user_id == self.user_id)\
1520 .filter(UserNotice.notice_read == false())\
1521 .all()
1522
1523 try:
1524 for entry in notices:
1525
1526 msg = {
1527 'msg_id': entry.user_notice_id,
1528 'level': entry.notification_level,
1529 'subject': entry.notice_subject,
1530 'body': entry.notice_body,
1531 }
1532 notice_messages.append(msg)
1533
1534 log.debug('Got user %s %s messages', self, len(notice_messages))
1535
1536 levels = [x['level'] for x in notice_messages]
1537 notice_level = 'notice-error' if 'error' in levels else 'notice-warning'
1538 except Exception:
1539 pass
1540
1541 return notice_messages, notice_level
1542
1458 def __repr__(self):
1543 def __repr__(self):
1459 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1544 return self.repr_user(self.user_id, self.username, self.ip_addr, self.is_authenticated)
1460 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1461
1545
1462 def set_authenticated(self, authenticated=True):
1546 def set_authenticated(self, authenticated=True):
1463 if self.user_id != self.anonymous_user.user_id:
1547 if self.user_id != self.anonymous_user.user_id:
@@ -1472,6 +1556,11 b' class AuthUser(object):'
1472 }
1556 }
1473
1557
1474 @classmethod
1558 @classmethod
1559 def repr_user(cls, user_id=0, username='ANONYMOUS', ip='0.0.0.0', is_authenticated=False):
1560 tmpl = "<AuthUser('id:{}[{}] ip:{} auth:{}')>"
1561 return tmpl.format(user_id, username, ip, is_authenticated)
1562
1563 @classmethod
1475 def from_cookie_store(cls, cookie_store):
1564 def from_cookie_store(cls, cookie_store):
1476 """
1565 """
1477 Creates AuthUser from a cookie store
1566 Creates AuthUser from a cookie store
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -283,7 +283,7 b' def get_current_lang(request):'
283 return getattr(request, '_LOCALE_', request.locale_name)
283 return getattr(request, '_LOCALE_', request.locale_name)
284
284
285
285
286 def attach_context_attributes(context, request, user_id=None):
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
287 """
287 """
288 Attach variables into template context called `c`.
288 Attach variables into template context called `c`.
289 """
289 """
@@ -379,7 +379,8 b' def attach_context_attributes(context, r'
379 "sideside": "sideside"
379 "sideside": "sideside"
380 }.get(request.GET.get('diffmode'))
380 }.get(request.GET.get('diffmode'))
381
381
382 is_api = hasattr(request, 'rpc_user')
382 if is_api is not None:
383 is_api = hasattr(request, 'rpc_user')
383 session_attrs = {
384 session_attrs = {
384 # defaults
385 # defaults
385 "clone_url_format": "http",
386 "clone_url_format": "http",
@@ -436,7 +437,7 b' def attach_context_attributes(context, r'
436
437
437 context.csrf_token = csrf_token
438 context.csrf_token = csrf_token
438 context.backends = rhodecode.BACKENDS.keys()
439 context.backends = rhodecode.BACKENDS.keys()
439 context.backends.sort()
440
440 unread_count = 0
441 unread_count = 0
441 user_bookmark_list = []
442 user_bookmark_list = []
442 if user_id:
443 if user_id:
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,6 +49,7 b' def send_email(recipients, subject, body'
49 :param subject: subject of the mail
49 :param subject: subject of the mail
50 :param body: body of the mail
50 :param body: body of the mail
51 :param html_body: html version of body
51 :param html_body: html version of body
52 :param email_config: specify custom configuration for mailer
52 """
53 """
53 log = get_logger(send_email)
54 log = get_logger(send_email)
54
55
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -12,10 +12,11 b' from sqlalchemy import __version__ as _s'
12
12
13 warnings.simplefilter('always', DeprecationWarning)
13 warnings.simplefilter('always', DeprecationWarning)
14
14
15 _sa_version = tuple(int(re.match("\d+", x).group(0))
15 _sa_version = tuple(int(re.match("\d+", x).group(0)) for x in _sa_version.split("."))
16 for x in _sa_version.split("."))
17 SQLA_07 = _sa_version >= (0, 7)
16 SQLA_07 = _sa_version >= (0, 7)
18 SQLA_08 = _sa_version >= (0, 8)
17 SQLA_08 = _sa_version >= (0, 8)
18 SQLA_09 = _sa_version >= (0, 9)
19 SQLA_10 = _sa_version >= (1, 0)
19
20
20 del re
21 del re
21 del _sa_version
22 del _sa_version
@@ -23,8 +24,8 b' del _sa_version'
23 from rhodecode.lib.dbmigrate.migrate.changeset.schema import *
24 from rhodecode.lib.dbmigrate.migrate.changeset.schema import *
24 from rhodecode.lib.dbmigrate.migrate.changeset.constraint import *
25 from rhodecode.lib.dbmigrate.migrate.changeset.constraint import *
25
26
26 sqlalchemy.schema.Table.__bases__ += (ChangesetTable,)
27 sqlalchemy.schema.Table.__bases__ += (ChangesetTable, )
27 sqlalchemy.schema.Column.__bases__ += (ChangesetColumn,)
28 sqlalchemy.schema.Column.__bases__ += (ChangesetColumn, )
28 sqlalchemy.schema.Index.__bases__ += (ChangesetIndex,)
29 sqlalchemy.schema.Index.__bases__ += (ChangesetIndex, )
29
30
30 sqlalchemy.schema.DefaultClause.__bases__ += (ChangesetDefaultClause,)
31 sqlalchemy.schema.DefaultClause.__bases__ += (ChangesetDefaultClause, )
@@ -97,7 +97,6 b' class ANSIColumnGenerator(AlterTableVisi'
97
97
98 table = self.start_alter_table(column)
98 table = self.start_alter_table(column)
99 self.append("ADD ")
99 self.append("ADD ")
100
101 self.append(self.get_column_specification(column))
100 self.append(self.get_column_specification(column))
102
101
103 for cons in column.constraints:
102 for cons in column.constraints:
@@ -111,7 +111,7 b' class ForeignKeyConstraint(ConstraintCha'
111 refcolnames, reftable = self._normalize_columns(refcolumns,
111 refcolnames, reftable = self._normalize_columns(refcolumns,
112 table_name=True)
112 table_name=True)
113 super(ForeignKeyConstraint, self).__init__(
113 super(ForeignKeyConstraint, self).__init__(
114 colnames, refcolnames, *args,**kwargs
114 colnames, refcolnames, *args, **kwargs
115 )
115 )
116 if table is not None:
116 if table is not None:
117 self._set_parent(table)
117 self._set_parent(table)
@@ -3,16 +3,20 b''
3
3
4 .. _`SQLite`: http://www.sqlite.org/
4 .. _`SQLite`: http://www.sqlite.org/
5 """
5 """
6 from UserDict import DictMixin
6 try: # Python 3
7 from collections.abc import MutableMapping as DictMixin
8 except ImportError: # Python 2
9 from UserDict import DictMixin
7 from copy import copy
10 from copy import copy
8 import re
11 import re
9
12
10 from sqlalchemy.databases import sqlite as sa_base
13 from sqlalchemy.databases import sqlite as sa_base
14 from sqlalchemy.schema import ForeignKeyConstraint
11 from sqlalchemy.schema import UniqueConstraint
15 from sqlalchemy.schema import UniqueConstraint
12
16
13 from rhodecode.lib.dbmigrate.migrate import exceptions
17 from rhodecode.lib.dbmigrate.migrate import exceptions
14 from rhodecode.lib.dbmigrate.migrate.changeset import ansisql
18 from rhodecode.lib.dbmigrate.migrate.changeset import ansisql
15
19 import sqlite3
16
20
17 SQLiteSchemaGenerator = sa_base.SQLiteDDLCompiler
21 SQLiteSchemaGenerator = sa_base.SQLiteDDLCompiler
18
22
@@ -73,10 +77,16 b' class SQLiteHelper(SQLiteCommon):'
73 cons for cons in table.constraints
77 cons for cons in table.constraints
74 if omit_uniques is None or cons.name not in omit_uniques
78 if omit_uniques is None or cons.name not in omit_uniques
75 ])
79 ])
80 tup = sqlite3.sqlite_version_info
81 if tup[0] > 3 or (tup[0] == 3 and tup[1] >= 26):
82 self.append('PRAGMA legacy_alter_table = ON')
83 self.execute()
76
84
77 self.append('ALTER TABLE %s RENAME TO migration_tmp' % table_name)
85 self.append('ALTER TABLE %s RENAME TO migration_tmp' % table_name)
78 self.execute()
86 self.execute()
79
87 if tup[0] > 3 or (tup[0] == 3 and tup[1] >= 26):
88 self.append('PRAGMA legacy_alter_table = OFF')
89 self.execute()
80 insertion_string = self._modify_table(table, column, delta)
90 insertion_string = self._modify_table(table, column, delta)
81
91
82 table.create(bind=self.connection)
92 table.create(bind=self.connection)
@@ -1,10 +1,13 b''
1 """
1 """
2 Schema module providing common schema operations.
2 Schema module providing common schema operations.
3 """
3 """
4 import abc
5 try: # Python 3
6 from collections.abc import MutableMapping as DictMixin
7 except ImportError: # Python 2
8 from UserDict import DictMixin
4 import warnings
9 import warnings
5
10
6 from UserDict import DictMixin
7
8 import sqlalchemy
11 import sqlalchemy
9
12
10 from sqlalchemy.schema import ForeignKeyConstraint
13 from sqlalchemy.schema import ForeignKeyConstraint
@@ -1,6 +1,17 b''
1 """
1 """
2 Safe quoting method
2 Safe quoting method
3 """
3 """
4 from rhodecode.lib.dbmigrate.migrate.changeset import SQLA_10
5
6
7 def fk_column_names(constraint):
8 if SQLA_10:
9 return [
10 constraint.columns[key].name for key in constraint.column_keys]
11 else:
12 return [
13 element.parent.name for element in constraint.elements]
14
4
15
5 def safe_quote(obj):
16 def safe_quote(obj):
6 # this is the SQLA 0.9 approach
17 # this is the SQLA 0.9 approach
@@ -27,6 +27,10 b' class InvalidVersionError(ControlledSche'
27 """Invalid version number."""
27 """Invalid version number."""
28
28
29
29
30 class VersionNotFoundError(KeyError):
31 """Specified version is not present."""
32
33
30 class DatabaseNotControlledError(ControlledSchemaError):
34 class DatabaseNotControlledError(ControlledSchemaError):
31 """Database should be under version control, but it's not."""
35 """Database should be under version control, but it's not."""
32
36
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -444,7 +444,8 b' class DiffProcessor(object):'
444 return diff_container(sorted(_files, key=sorter))
444 return diff_container(sorted(_files, key=sorter))
445
445
446 def _check_large_diff(self):
446 def _check_large_diff(self):
447 log.debug('Diff exceeds current diff_limit of %s', self.diff_limit)
447 if self.diff_limit:
448 log.debug('Checking if diff exceeds current diff_limit of %s', self.diff_limit)
448 if not self.show_full_diff and (self.cur_diff_size > self.diff_limit):
449 if not self.show_full_diff and (self.cur_diff_size > self.diff_limit):
449 raise DiffLimitExceeded('Diff Limit `%s` Exceeded', self.diff_limit)
450 raise DiffLimitExceeded('Diff Limit `%s` Exceeded', self.diff_limit)
450
451
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -27,7 +27,6 b' import traceback'
27 import tempfile
27 import tempfile
28 import glob
28 import glob
29
29
30
31 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
32
31
33 # NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
32 # NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
@@ -35,7 +34,7 b" global_prefix = 'rhodecode'"
35 exc_store_dir_name = 'rc_exception_store_v1'
34 exc_store_dir_name = 'rc_exception_store_v1'
36
35
37
36
38 def exc_serialize(exc_id, tb, exc_type):
37 def exc_serialize(exc_id, tb, exc_type, extra_data=None):
39
38
40 data = {
39 data = {
41 'version': 'v1',
40 'version': 'v1',
@@ -45,6 +44,8 b' def exc_serialize(exc_id, tb, exc_type):'
45 'exc_message': tb,
44 'exc_message': tb,
46 'exc_type': exc_type,
45 'exc_type': exc_type,
47 }
46 }
47 if extra_data:
48 data.update(extra_data)
48 return msgpack.packb(data), data
49 return msgpack.packb(data), data
49
50
50
51
@@ -77,13 +78,24 b' def get_exc_store():'
77 return _exc_store_path
78 return _exc_store_path
78
79
79
80
80 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
81 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix, send_email=None):
81 """
82 """
82 Low level function to store exception in the exception tracker
83 Low level function to store exception in the exception tracker
83 """
84 """
85 from pyramid.threadlocal import get_current_request
86 import rhodecode as app
87 request = get_current_request()
88 extra_data = {}
89 # NOTE(marcink): store request information into exc_data
90 if request:
91 extra_data['client_address'] = getattr(request, 'client_addr', '')
92 extra_data['user_agent'] = getattr(request, 'user_agent', '')
93 extra_data['method'] = getattr(request, 'method', '')
94 extra_data['url'] = getattr(request, 'url', '')
84
95
85 exc_store_path = get_exc_store()
96 exc_store_path = get_exc_store()
86 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
97 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name, extra_data=extra_data)
98
87 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
99 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
88 if not os.path.isdir(exc_store_path):
100 if not os.path.isdir(exc_store_path):
89 os.makedirs(exc_store_path)
101 os.makedirs(exc_store_path)
@@ -92,6 +104,52 b' def _store_exception(exc_id, exc_type_na'
92 f.write(exc_data)
104 f.write(exc_data)
93 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
105 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
94
106
107 if send_email is None:
108 # NOTE(marcink): read app config unless we specify explicitly
109 send_email = app.CONFIG.get('exception_tracker.send_email', False)
110
111 mail_server = app.CONFIG.get('smtp_server') or None
112 send_email = send_email and mail_server
113 if send_email:
114 try:
115 send_exc_email(request, exc_id, exc_type_name)
116 except Exception:
117 log.exception('Failed to send exception email')
118 pass
119
120
121 def send_exc_email(request, exc_id, exc_type_name):
122 import rhodecode as app
123 from rhodecode.apps._base import TemplateArgs
124 from rhodecode.lib.utils2 import aslist
125 from rhodecode.lib.celerylib import run_task, tasks
126 from rhodecode.lib.base import attach_context_attributes
127 from rhodecode.model.notification import EmailNotificationModel
128
129 recipients = aslist(app.CONFIG.get('exception_tracker.send_email_recipients', ''))
130 log.debug('Sending Email exception to: `%s`', recipients or 'all super admins')
131
132 # NOTE(marcink): needed for email template rendering
133 user_id = None
134 if request:
135 user_id = request.user.user_id
136 attach_context_attributes(TemplateArgs(), request, user_id=user_id, is_api=True)
137
138 email_kwargs = {
139 'email_prefix': app.CONFIG.get('exception_tracker.email_prefix', '') or '[RHODECODE ERROR]',
140 'exc_url': request.route_url('admin_settings_exception_tracker_show', exception_id=exc_id),
141 'exc_id': exc_id,
142 'exc_type_name': exc_type_name,
143 'exc_traceback': read_exception(exc_id, prefix=None),
144 }
145
146 (subject, headers, email_body,
147 email_body_plaintext) = EmailNotificationModel().render_email(
148 EmailNotificationModel.TYPE_EMAIL_EXCEPTION, **email_kwargs)
149
150 run_task(tasks.send_email, recipients, subject,
151 email_body_plaintext, email_body)
152
95
153
96 def _prepare_exception(exc_info):
154 def _prepare_exception(exc_info):
97 exc_type, exc_value, exc_traceback = exc_info
155 exc_type, exc_value, exc_traceback = exc_info
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -58,6 +58,10 b' class UserOwnsUserGroupsException(Except'
58 pass
58 pass
59
59
60
60
61 class UserOwnsPullRequestsException(Exception):
62 pass
63
64
61 class UserOwnsArtifactsException(Exception):
65 class UserOwnsArtifactsException(Exception):
62 pass
66 pass
63
67
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -34,6 +34,8 b' from __future__ import unicode_literals'
34
34
35 import datetime
35 import datetime
36 from StringIO import StringIO
36 from StringIO import StringIO
37
38 import pytz
37 from six.moves.urllib import parse as urlparse
39 from six.moves.urllib import parse as urlparse
38
40
39 from rhodecode.lib.feedgenerator import datetime_safe
41 from rhodecode.lib.feedgenerator import datetime_safe
@@ -227,13 +229,13 b' class SyndicationFeed(object):'
227 latest_date = item_date
229 latest_date = item_date
228
230
229 # datetime.now(tz=utc) is slower, as documented in django.utils.timezone.now
231 # datetime.now(tz=utc) is slower, as documented in django.utils.timezone.now
230 return latest_date or datetime.datetime.utcnow().replace(tzinfo=utc)
232 return latest_date or datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
231
233
232
234
233 class Enclosure(object):
235 class Enclosure(object):
234 "Represents an RSS enclosure"
236 """Represents an RSS enclosure"""
235 def __init__(self, url, length, mime_type):
237 def __init__(self, url, length, mime_type):
236 "All args are expected to be Python Unicode objects"
238 """All args are expected to be Python Unicode objects"""
237 self.length, self.mime_type = length, mime_type
239 self.length, self.mime_type = length, mime_type
238 self.url = iri_to_uri(url)
240 self.url = iri_to_uri(url)
239
241
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -253,7 +253,7 b' class _ToolTip(object):'
253
253
254 tooltip = _ToolTip()
254 tooltip = _ToolTip()
255
255
256 files_icon = u'<i class="file-breadcrumb-copy tooltip icon-clipboard clipboard-action" data-clipboard-text="{}" title="Copy the full path"></i>'
256 files_icon = u'<i class="file-breadcrumb-copy tooltip icon-clipboard clipboard-action" data-clipboard-text="{}" title="Copy file path"></i>'
257
257
258
258
259 def files_breadcrumbs(repo_name, commit_id, file_path, at_ref=None, limit_items=False, linkify_last_item=False):
259 def files_breadcrumbs(repo_name, commit_id, file_path, at_ref=None, limit_items=False, linkify_last_item=False):
@@ -1617,18 +1617,19 b' def render_binary(repo_name, file_obj):'
1617 Choose how to render a binary file
1617 Choose how to render a binary file
1618 """
1618 """
1619
1619
1620 # unicode
1620 filename = file_obj.name
1621 filename = file_obj.name
1621
1622
1622 # images
1623 # images
1623 for ext in ['*.png', '*.jpg', '*.ico', '*.gif']:
1624 for ext in ['*.png', '*.jpeg', '*.jpg', '*.ico', '*.gif']:
1624 if fnmatch.fnmatch(filename, pat=ext):
1625 if fnmatch.fnmatch(filename, pat=ext):
1625 alt = escape(filename)
1626 src = route_path(
1626 src = route_path(
1627 'repo_file_raw', repo_name=repo_name,
1627 'repo_file_raw', repo_name=repo_name,
1628 commit_id=file_obj.commit.raw_id,
1628 commit_id=file_obj.commit.raw_id,
1629 f_path=file_obj.path)
1629 f_path=file_obj.path)
1630
1630 return literal(
1631 return literal(
1631 '<img class="rendered-binary" alt="{}" src="{}">'.format(alt, src))
1632 '<img class="rendered-binary" alt="rendered-image" src="{}">'.format(src))
1632
1633
1633
1634
1634 def renderer_from_filename(filename, exclude=None):
1635 def renderer_from_filename(filename, exclude=None):
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,7 +24,6 b' Set of hooks run by RhodeCode Enterprise'
24 """
24 """
25
25
26 import os
26 import os
27 import collections
28 import logging
27 import logging
29
28
30 import rhodecode
29 import rhodecode
@@ -349,8 +348,8 b' class ExtensionCallback(object):'
349 try:
348 try:
350 kwargs_to_pass[key] = kwargs[key]
349 kwargs_to_pass[key] = kwargs[key]
351 except KeyError:
350 except KeyError:
352 log.error('Failed to fetch %s key. Expected keys: %s',
351 log.error('Failed to fetch %s key from given kwargs. '
353 key, self._kwargs_keys)
352 'Expected keys: %s', key, self._kwargs_keys)
354 raise
353 raise
355
354
356 # backward compat for removed api_key for old hooks. This was it works
355 # backward compat for removed api_key for old hooks. This was it works
@@ -437,6 +436,15 b' log_review_pull_request = ExtensionCallb'
437 'mergeable', 'source', 'target', 'author', 'reviewers'))
436 'mergeable', 'source', 'target', 'author', 'reviewers'))
438
437
439
438
439 log_comment_pull_request = ExtensionCallback(
440 hook_name='COMMENT_PULL_REQUEST',
441 kwargs_keys=(
442 'server_url', 'config', 'scm', 'username', 'ip', 'action',
443 'repository', 'pull_request_id', 'url', 'title', 'description',
444 'status', 'comment', 'created_on', 'updated_on', 'commit_ids', 'review_status',
445 'mergeable', 'source', 'target', 'author', 'reviewers'))
446
447
440 log_update_pull_request = ExtensionCallback(
448 log_update_pull_request = ExtensionCallback(
441 hook_name='UPDATE_PULL_REQUEST',
449 hook_name='UPDATE_PULL_REQUEST',
442 kwargs_keys=(
450 kwargs_keys=(
@@ -484,6 +492,15 b' log_delete_repository = ExtensionCallbac'
484 'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on'))
492 'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on'))
485
493
486
494
495 log_comment_commit_repository = ExtensionCallback(
496 hook_name='COMMENT_COMMIT_REPO_HOOK',
497 kwargs_keys=(
498 'repo_name', 'repo_type', 'description', 'private', 'created_on',
499 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
500 'clone_uri', 'fork_id', 'group_id',
501 'repository', 'created_by', 'comment', 'commit'))
502
503
487 log_create_repository_group = ExtensionCallback(
504 log_create_repository_group = ExtensionCallback(
488 hook_name='CREATE_REPO_GROUP_HOOK',
505 hook_name='CREATE_REPO_GROUP_HOOK',
489 kwargs_keys=(
506 kwargs_keys=(
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,144 +26,190 b' from rhodecode.lib import hooks_base'
26 from rhodecode.lib import utils2
26 from rhodecode.lib import utils2
27
27
28
28
29 def _get_rc_scm_extras(username, repo_name, repo_alias, action):
29 def _supports_repo_type(repo_type):
30 # TODO: johbo: Replace by vcs_operation_context and remove fully
30 if repo_type in ('hg', 'git'):
31 return True
32 return False
33
34
35 def _get_vcs_operation_context(username, repo_name, repo_type, action):
36 # NOTE(dan): import loop
31 from rhodecode.lib.base import vcs_operation_context
37 from rhodecode.lib.base import vcs_operation_context
38
32 check_locking = action in ('pull', 'push')
39 check_locking = action in ('pull', 'push')
33
40
34 request = get_current_request()
41 request = get_current_request()
35
42
36 # default
37 dummy_environ = webob.Request.blank('').environ
38 try:
43 try:
39 environ = request.environ or dummy_environ
44 environ = request.environ
40 except TypeError:
45 except TypeError:
41 # we might use this outside of request context
46 # we might use this outside of request context
42 environ = dummy_environ
47 environ = {}
43
48
44 extras = vcs_operation_context(
49 if not environ:
45 environ, repo_name, username, action, repo_alias, check_locking)
50 environ = webob.Request.blank('').environ
51
52 extras = vcs_operation_context(environ, repo_name, username, action, repo_type, check_locking)
46 return utils2.AttributeDict(extras)
53 return utils2.AttributeDict(extras)
47
54
48
55
49 def trigger_post_push_hook(
56 def trigger_post_push_hook(username, action, hook_type, repo_name, repo_type, commit_ids):
50 username, action, hook_type, repo_name, repo_alias, commit_ids):
51 """
57 """
52 Triggers push action hooks
58 Triggers push action hooks
53
59
54 :param username: username who pushes
60 :param username: username who pushes
55 :param action: push/push_local/push_remote
61 :param action: push/push_local/push_remote
62 :param hook_type: type of hook executed
56 :param repo_name: name of repo
63 :param repo_name: name of repo
57 :param repo_alias: the type of SCM repo
64 :param repo_type: the type of SCM repo
58 :param commit_ids: list of commit ids that we pushed
65 :param commit_ids: list of commit ids that we pushed
59 """
66 """
60 extras = _get_rc_scm_extras(username, repo_name, repo_alias, action)
67 extras = _get_vcs_operation_context(username, repo_name, repo_type, action)
61 extras.commit_ids = commit_ids
68 extras.commit_ids = commit_ids
62 extras.hook_type = hook_type
69 extras.hook_type = hook_type
63 hooks_base.post_push(extras)
70 hooks_base.post_push(extras)
64
71
65
72
66 def trigger_log_create_pull_request_hook(username, repo_name, repo_alias,
73 def trigger_comment_commit_hooks(username, repo_name, repo_type, repo, data=None):
67 pull_request, data=None):
74 """
75 Triggers when a comment is made on a commit
76
77 :param username: username who creates the comment
78 :param repo_name: name of target repo
79 :param repo_type: the type of SCM target repo
80 :param repo: the repo object we trigger the event for
81 :param data: extra data for specific events e.g {'comment': comment_obj, 'commit': commit_obj}
82 """
83 if not _supports_repo_type(repo_type):
84 return
85
86 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'comment_commit')
87
88 comment = data['comment']
89 commit = data['commit']
90
91 events.trigger(events.RepoCommitCommentEvent(repo, commit, comment))
92 extras.update(repo.get_dict())
93
94 extras.commit = commit.serialize()
95 extras.comment = comment.get_api_data()
96 extras.created_by = username
97 hooks_base.log_comment_commit_repository(**extras)
98
99
100 def trigger_create_pull_request_hook(username, repo_name, repo_type, pull_request, data=None):
68 """
101 """
69 Triggers create pull request action hooks
102 Triggers create pull request action hooks
70
103
71 :param username: username who creates the pull request
104 :param username: username who creates the pull request
72 :param repo_name: name of target repo
105 :param repo_name: name of target repo
73 :param repo_alias: the type of SCM target repo
106 :param repo_type: the type of SCM target repo
74 :param pull_request: the pull request that was created
107 :param pull_request: the pull request that was created
75 :param data: extra data for specific events e.g {'comment': comment_obj}
108 :param data: extra data for specific events e.g {'comment': comment_obj}
76 """
109 """
77 if repo_alias not in ('hg', 'git'):
110 if not _supports_repo_type(repo_type):
78 return
111 return
79
112
80 extras = _get_rc_scm_extras(username, repo_name, repo_alias,
113 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'create_pull_request')
81 'create_pull_request')
82 events.trigger(events.PullRequestCreateEvent(pull_request))
114 events.trigger(events.PullRequestCreateEvent(pull_request))
83 extras.update(pull_request.get_api_data(with_merge_state=False))
115 extras.update(pull_request.get_api_data(with_merge_state=False))
84 hooks_base.log_create_pull_request(**extras)
116 hooks_base.log_create_pull_request(**extras)
85
117
86
118
87 def trigger_log_merge_pull_request_hook(username, repo_name, repo_alias,
119 def trigger_merge_pull_request_hook(username, repo_name, repo_type, pull_request, data=None):
88 pull_request, data=None):
89 """
120 """
90 Triggers merge pull request action hooks
121 Triggers merge pull request action hooks
91
122
92 :param username: username who creates the pull request
123 :param username: username who creates the pull request
93 :param repo_name: name of target repo
124 :param repo_name: name of target repo
94 :param repo_alias: the type of SCM target repo
125 :param repo_type: the type of SCM target repo
95 :param pull_request: the pull request that was merged
126 :param pull_request: the pull request that was merged
96 :param data: extra data for specific events e.g {'comment': comment_obj}
127 :param data: extra data for specific events e.g {'comment': comment_obj}
97 """
128 """
98 if repo_alias not in ('hg', 'git'):
129 if not _supports_repo_type(repo_type):
99 return
130 return
100
131
101 extras = _get_rc_scm_extras(username, repo_name, repo_alias,
132 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'merge_pull_request')
102 'merge_pull_request')
103 events.trigger(events.PullRequestMergeEvent(pull_request))
133 events.trigger(events.PullRequestMergeEvent(pull_request))
104 extras.update(pull_request.get_api_data())
134 extras.update(pull_request.get_api_data())
105 hooks_base.log_merge_pull_request(**extras)
135 hooks_base.log_merge_pull_request(**extras)
106
136
107
137
108 def trigger_log_close_pull_request_hook(username, repo_name, repo_alias,
138 def trigger_close_pull_request_hook(username, repo_name, repo_type, pull_request, data=None):
109 pull_request, data=None):
110 """
139 """
111 Triggers close pull request action hooks
140 Triggers close pull request action hooks
112
141
113 :param username: username who creates the pull request
142 :param username: username who creates the pull request
114 :param repo_name: name of target repo
143 :param repo_name: name of target repo
115 :param repo_alias: the type of SCM target repo
144 :param repo_type: the type of SCM target repo
116 :param pull_request: the pull request that was closed
145 :param pull_request: the pull request that was closed
117 :param data: extra data for specific events e.g {'comment': comment_obj}
146 :param data: extra data for specific events e.g {'comment': comment_obj}
118 """
147 """
119 if repo_alias not in ('hg', 'git'):
148 if not _supports_repo_type(repo_type):
120 return
149 return
121
150
122 extras = _get_rc_scm_extras(username, repo_name, repo_alias,
151 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'close_pull_request')
123 'close_pull_request')
124 events.trigger(events.PullRequestCloseEvent(pull_request))
152 events.trigger(events.PullRequestCloseEvent(pull_request))
125 extras.update(pull_request.get_api_data())
153 extras.update(pull_request.get_api_data())
126 hooks_base.log_close_pull_request(**extras)
154 hooks_base.log_close_pull_request(**extras)
127
155
128
156
129 def trigger_log_review_pull_request_hook(username, repo_name, repo_alias,
157 def trigger_review_pull_request_hook(username, repo_name, repo_type, pull_request, data=None):
130 pull_request, data=None):
131 """
158 """
132 Triggers review status change pull request action hooks
159 Triggers review status change pull request action hooks
133
160
134 :param username: username who creates the pull request
161 :param username: username who creates the pull request
135 :param repo_name: name of target repo
162 :param repo_name: name of target repo
136 :param repo_alias: the type of SCM target repo
163 :param repo_type: the type of SCM target repo
137 :param pull_request: the pull request that review status changed
164 :param pull_request: the pull request that review status changed
138 :param data: extra data for specific events e.g {'comment': comment_obj}
165 :param data: extra data for specific events e.g {'comment': comment_obj}
139 """
166 """
140 if repo_alias not in ('hg', 'git'):
167 if not _supports_repo_type(repo_type):
141 return
168 return
142
169
143 extras = _get_rc_scm_extras(username, repo_name, repo_alias,
170 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'review_pull_request')
144 'review_pull_request')
145 status = data.get('status')
171 status = data.get('status')
146 events.trigger(events.PullRequestReviewEvent(pull_request, status))
172 events.trigger(events.PullRequestReviewEvent(pull_request, status))
147 extras.update(pull_request.get_api_data())
173 extras.update(pull_request.get_api_data())
148 hooks_base.log_review_pull_request(**extras)
174 hooks_base.log_review_pull_request(**extras)
149
175
150
176
151 def trigger_log_update_pull_request_hook(username, repo_name, repo_alias,
177 def trigger_comment_pull_request_hook(username, repo_name, repo_type, pull_request, data=None):
152 pull_request, data=None):
178 """
179 Triggers when a comment is made on a pull request
180
181 :param username: username who creates the pull request
182 :param repo_name: name of target repo
183 :param repo_type: the type of SCM target repo
184 :param pull_request: the pull request that comment was made on
185 :param data: extra data for specific events e.g {'comment': comment_obj}
186 """
187 if not _supports_repo_type(repo_type):
188 return
189
190 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'comment_pull_request')
191
192 comment = data['comment']
193 events.trigger(events.PullRequestCommentEvent(pull_request, comment))
194 extras.update(pull_request.get_api_data())
195 extras.comment = comment.get_api_data()
196 hooks_base.log_comment_pull_request(**extras)
197
198
199 def trigger_update_pull_request_hook(username, repo_name, repo_type, pull_request, data=None):
153 """
200 """
154 Triggers update pull request action hooks
201 Triggers update pull request action hooks
155
202
156 :param username: username who creates the pull request
203 :param username: username who creates the pull request
157 :param repo_name: name of target repo
204 :param repo_name: name of target repo
158 :param repo_alias: the type of SCM target repo
205 :param repo_type: the type of SCM target repo
159 :param pull_request: the pull request that was updated
206 :param pull_request: the pull request that was updated
160 :param data: extra data for specific events e.g {'comment': comment_obj}
207 :param data: extra data for specific events e.g {'comment': comment_obj}
161 """
208 """
162 if repo_alias not in ('hg', 'git'):
209 if not _supports_repo_type(repo_type):
163 return
210 return
164
211
165 extras = _get_rc_scm_extras(username, repo_name, repo_alias,
212 extras = _get_vcs_operation_context(username, repo_name, repo_type, 'update_pull_request')
166 'update_pull_request')
167 events.trigger(events.PullRequestUpdateEvent(pull_request))
213 events.trigger(events.PullRequestUpdateEvent(pull_request))
168 extras.update(pull_request.get_api_data())
214 extras.update(pull_request.get_api_data())
169 hooks_base.log_update_pull_request(**extras)
215 hooks_base.log_update_pull_request(**extras)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,8 +22,9 b' import time'
22 import logging
22 import logging
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.lib.auth import AuthUser
25 from rhodecode.lib.base import get_ip_addr, get_access_path, get_user_agent
26 from rhodecode.lib.base import get_ip_addr, get_access_path, get_user_agent
26 from rhodecode.lib.utils2 import safe_str
27 from rhodecode.lib.utils2 import safe_str, get_current_rhodecode_user
27
28
28
29
29 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
@@ -46,9 +47,11 b' class RequestWrapperTween(object):'
46 total = end - start
47 total = end - start
47 count = request.request_count()
48 count = request.request_count()
48 _ver_ = rhodecode.__version__
49 _ver_ = rhodecode.__version__
50 default_user_info = AuthUser.repr_user(ip=get_ip_addr(request.environ))
51 user_info = get_current_rhodecode_user(request) or default_user_info
49 log.info(
52 log.info(
50 'Req[%4s] IP: %s %s Request to %s time: %.4fs [%s], RhodeCode %s',
53 'Req[%4s] %s %s Request to %s time: %.4fs [%s], RhodeCode %s',
51 count, get_ip_addr(request.environ), request.environ.get('REQUEST_METHOD'),
54 count, user_info, request.environ.get('REQUEST_METHOD'),
52 safe_str(get_access_path(request.environ)), total,
55 safe_str(get_access_path(request.environ)), total,
53 get_user_agent(request. environ), _ver_
56 get_user_agent(request. environ), _ver_
54 )
57 )
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -79,6 +79,7 b' class VcsHttpProxy(object):'
79 self._repo_name = repo_name
79 self._repo_name = repo_name
80 self._repo_path = repo_path
80 self._repo_path = repo_path
81 self._config = config
81 self._config = config
82 self.rc_extras = {}
82 log.debug(
83 log.debug(
83 "Creating VcsHttpProxy for repo %s, url %s",
84 "Creating VcsHttpProxy for repo %s, url %s",
84 repo_name, url)
85 repo_name, url)
@@ -87,14 +88,23 b' class VcsHttpProxy(object):'
87 config = msgpack.packb(self._config)
88 config = msgpack.packb(self._config)
88 request = webob.request.Request(environ)
89 request = webob.request.Request(environ)
89 request_headers = request.headers
90 request_headers = request.headers
91
90 request_headers.update({
92 request_headers.update({
91 # TODO: johbo: Remove this, rely on URL path only
93 # TODO: johbo: Remove this, rely on URL path only
92 'X-RC-Repo-Name': self._repo_name,
94 'X-RC-Repo-Name': self._repo_name,
93 'X-RC-Repo-Path': self._repo_path,
95 'X-RC-Repo-Path': self._repo_path,
94 'X-RC-Path-Info': environ['PATH_INFO'],
96 'X-RC-Path-Info': environ['PATH_INFO'],
97
98 'X-RC-Repo-Store': self.rc_extras.get('repo_store'),
99 'X-RC-Server-Config-File': self.rc_extras.get('config'),
100
101 'X-RC-Auth-User': self.rc_extras.get('username'),
102 'X-RC-Auth-User-Id': self.rc_extras.get('user_id'),
103 'X-RC-Auth-User-Ip': self.rc_extras.get('ip'),
104
95 # TODO: johbo: Avoid encoding and put this into payload?
105 # TODO: johbo: Avoid encoding and put this into payload?
96 'X-RC-Repo-Config': base64.b64encode(config),
106 'X-RC-Repo-Config': base64.b64encode(config),
97 'X-RC-Locked-Status-Code': rhodecode.CONFIG.get('lock_ret_code')
107 'X-RC-Locked-Status-Code': rhodecode.CONFIG.get('lock_ret_code'),
98 })
108 })
99
109
100 method = environ['REQUEST_METHOD']
110 method = environ['REQUEST_METHOD']
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -877,7 +877,7 b' class _Page(list):'
877
877
878 # Below is RhodeCode custom code
878 # Below is RhodeCode custom code
879
879
880 # Copyright (C) 2010-2019 RhodeCode GmbH
880 # Copyright (C) 2010-2020 RhodeCode GmbH
881 #
881 #
882 # This program is free software: you can redistribute it and/or modify
882 # This program is free software: you can redistribute it and/or modify
883 # it under the terms of the GNU Affero General Public License, version 3
883 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2019 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -47,7 +47,7 b' from .utils import ('
47 FreshRegionCache, ActiveRegionCache)
47 FreshRegionCache, ActiveRegionCache)
48
48
49
49
50 FILE_TREE_CACHE_VER = 'v2'
50 FILE_TREE_CACHE_VER = 'v3'
51
51
52
52
53 def configure_dogpile_cache(settings):
53 def configure_dogpile_cache(settings):
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2019 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2019 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2019 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2019 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -143,8 +143,9 b' def python_info():'
143
143
144
144
145 def py_modules():
145 def py_modules():
146 mods = dict([(p.project_name, p.version)
146 mods = dict([(p.project_name, {'version': p.version, 'location': p.location})
147 for p in pkg_resources.working_set])
147 for p in pkg_resources.working_set])
148
148 value = sorted(mods.items(), key=lambda k: k[0].lower())
149 value = sorted(mods.items(), key=lambda k: k[0].lower())
149 return SysInfoRes(value=value)
150 return SysInfoRes(value=value)
150
151
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -688,14 +688,17 b' def get_clone_url(request, uri_tmpl, rep'
688 return safe_unicode(url)
688 return safe_unicode(url)
689
689
690
690
691 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
691 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None,
692 maybe_unreachable=False):
692 """
693 """
693 Safe version of get_commit if this commit doesn't exists for a
694 Safe version of get_commit if this commit doesn't exists for a
694 repository it returns a Dummy one instead
695 repository it returns a Dummy one instead
695
696
696 :param repo: repository instance
697 :param repo: repository instance
697 :param commit_id: commit id as str
698 :param commit_id: commit id as str
699 :param commit_idx: numeric commit index
698 :param pre_load: optional list of commit attributes to load
700 :param pre_load: optional list of commit attributes to load
701 :param maybe_unreachable: translate unreachable commits on git repos
699 """
702 """
700 # TODO(skreft): remove these circular imports
703 # TODO(skreft): remove these circular imports
701 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
704 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
@@ -706,7 +709,8 b' def get_commit_safe(repo, commit_id=None'
706
709
707 try:
710 try:
708 commit = repo.get_commit(
711 commit = repo.get_commit(
709 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
712 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load,
713 maybe_unreachable=maybe_unreachable)
710 except (RepositoryError, LookupError):
714 except (RepositoryError, LookupError):
711 commit = EmptyCommit()
715 commit = EmptyCommit()
712 return commit
716 return commit
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -216,6 +216,7 b' class MergeResponse(object):'
216 Return a human friendly error message for the given merge status code.
216 Return a human friendly error message for the given merge status code.
217 """
217 """
218 msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason])
218 msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason])
219
219 try:
220 try:
220 return msg.format(**self.metadata)
221 return msg.format(**self.metadata)
221 except Exception:
222 except Exception:
@@ -230,6 +231,14 b' class MergeResponse(object):'
230 return data
231 return data
231
232
232
233
234 class TargetRefMissing(ValueError):
235 pass
236
237
238 class SourceRefMissing(ValueError):
239 pass
240
241
233 class BaseRepository(object):
242 class BaseRepository(object):
234 """
243 """
235 Base Repository for final backends
244 Base Repository for final backends
@@ -437,7 +446,8 b' class BaseRepository(object):'
437 self._invalidate_prop_cache('commit_ids')
446 self._invalidate_prop_cache('commit_ids')
438 self._is_empty = False
447 self._is_empty = False
439
448
440 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None):
449 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
450 translate_tag=None, maybe_unreachable=False):
441 """
451 """
442 Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx`
452 Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx`
443 are both None, most recent commit is returned.
453 are both None, most recent commit is returned.
@@ -923,6 +933,9 b' class BaseCommit(object):'
923 d.pop('repository', None)
933 d.pop('repository', None)
924 return d
934 return d
925
935
936 def serialize(self):
937 return self.__json__()
938
926 def _get_refs(self):
939 def _get_refs(self):
927 return {
940 return {
928 'branches': [self.branch] if self.branch else [],
941 'branches': [self.branch] if self.branch else [],
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -307,15 +307,16 b' class GitCommit(base.BaseCommit):'
307 filenodes = []
307 filenodes = []
308
308
309 # extracted tree ID gives us our files...
309 # extracted tree ID gives us our files...
310 bytes_path = safe_str(path) # libgit operates on bytes
310 for name, stat_, id_, type_ in self._remote.tree_items(tree_id):
311 for name, stat_, id_, type_ in self._remote.tree_items(tree_id):
311 if type_ == 'link':
312 if type_ == 'link':
312 url = self._get_submodule_url('/'.join((path, name)))
313 url = self._get_submodule_url('/'.join((bytes_path, name)))
313 dirnodes.append(SubModuleNode(
314 dirnodes.append(SubModuleNode(
314 name, url=url, commit=id_, alias=self.repository.alias))
315 name, url=url, commit=id_, alias=self.repository.alias))
315 continue
316 continue
316
317
317 if path != '':
318 if bytes_path != '':
318 obj_path = '/'.join((path, name))
319 obj_path = '/'.join((bytes_path, name))
319 else:
320 else:
320 obj_path = name
321 obj_path = name
321 if obj_path not in self._stat_modes:
322 if obj_path not in self._stat_modes:
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -228,12 +228,13 b' class GitRepository(BaseRepository):'
228 return []
228 return []
229 return output.splitlines()
229 return output.splitlines()
230
230
231 def _lookup_commit(self, commit_id_or_idx, translate_tag=True):
231 def _lookup_commit(self, commit_id_or_idx, translate_tag=True, maybe_unreachable=False):
232 def is_null(value):
232 def is_null(value):
233 return len(value) == commit_id_or_idx.count('0')
233 return len(value) == commit_id_or_idx.count('0')
234
234
235 if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1):
235 if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1):
236 return self.commit_ids[-1]
236 return self.commit_ids[-1]
237
237 commit_missing_err = "Commit {} does not exist for `{}`".format(
238 commit_missing_err = "Commit {} does not exist for `{}`".format(
238 *map(safe_str, [commit_id_or_idx, self.name]))
239 *map(safe_str, [commit_id_or_idx, self.name]))
239
240
@@ -248,7 +249,8 b' class GitRepository(BaseRepository):'
248 elif is_bstr:
249 elif is_bstr:
249 # Need to call remote to translate id for tagging scenario
250 # Need to call remote to translate id for tagging scenario
250 try:
251 try:
251 remote_data = self._remote.get_object(commit_id_or_idx)
252 remote_data = self._remote.get_object(commit_id_or_idx,
253 maybe_unreachable=maybe_unreachable)
252 commit_id_or_idx = remote_data["commit_id"]
254 commit_id_or_idx = remote_data["commit_id"]
253 except (CommitDoesNotExistError,):
255 except (CommitDoesNotExistError,):
254 raise CommitDoesNotExistError(commit_missing_err)
256 raise CommitDoesNotExistError(commit_missing_err)
@@ -410,7 +412,8 b' class GitRepository(BaseRepository):'
410 except Exception:
412 except Exception:
411 return
413 return
412
414
413 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=True):
415 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
416 translate_tag=True, maybe_unreachable=False):
414 """
417 """
415 Returns `GitCommit` object representing commit from git repository
418 Returns `GitCommit` object representing commit from git repository
416 at the given `commit_id` or head (most recent commit) if None given.
419 at the given `commit_id` or head (most recent commit) if None given.
@@ -440,7 +443,7 b' class GitRepository(BaseRepository):'
440 commit_id = "tip"
443 commit_id = "tip"
441
444
442 if translate_tag:
445 if translate_tag:
443 commit_id = self._lookup_commit(commit_id)
446 commit_id = self._lookup_commit(commit_id, maybe_unreachable=maybe_unreachable)
444
447
445 try:
448 try:
446 idx = self._commit_ids[commit_id]
449 idx = self._commit_ids[commit_id]
@@ -577,6 +580,9 b' class GitRepository(BaseRepository):'
577 return len(self.commit_ids)
580 return len(self.commit_ids)
578
581
579 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
582 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
583 log.debug('Calculating common ancestor between %sc1:%s and %sc2:%s',
584 self, commit_id1, repo2, commit_id2)
585
580 if commit_id1 == commit_id2:
586 if commit_id1 == commit_id2:
581 return commit_id1
587 return commit_id1
582
588
@@ -597,6 +603,8 b' class GitRepository(BaseRepository):'
597 ['merge-base', commit_id1, commit_id2])
603 ['merge-base', commit_id1, commit_id2])
598 ancestor_id = re.findall(r'[0-9a-fA-F]{40}', output)[0]
604 ancestor_id = re.findall(r'[0-9a-fA-F]{40}', output)[0]
599
605
606 log.debug('Found common ancestor with sha: %s', ancestor_id)
607
600 return ancestor_id
608 return ancestor_id
601
609
602 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
610 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
@@ -662,6 +670,13 b' class GitRepository(BaseRepository):'
662 self._remote.remove_ref(ref_name)
670 self._remote.remove_ref(ref_name)
663 self._invalidate_prop_cache('_refs')
671 self._invalidate_prop_cache('_refs')
664
672
673 def run_gc(self, prune=True):
674 cmd = ['gc', '--aggressive']
675 if prune:
676 cmd += ['--prune=now']
677 _stdout, stderr = self.run_git_command(cmd, fail_on_stderr=False)
678 return stderr
679
665 def _update_server_info(self):
680 def _update_server_info(self):
666 """
681 """
667 runs gits update-server-info command in this repo instance
682 runs gits update-server-info command in this repo instance
@@ -827,8 +842,7 b' class GitRepository(BaseRepository):'
827
842
828 if self.is_empty():
843 if self.is_empty():
829 # TODO(skreft): do something more robust in this case.
844 # TODO(skreft): do something more robust in this case.
830 raise RepositoryError(
845 raise RepositoryError('Do not know how to merge into empty repositories yet')
831 'Do not know how to merge into empty repositories yet')
832 unresolved = None
846 unresolved = None
833
847
834 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
848 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
@@ -836,9 +850,11 b' class GitRepository(BaseRepository):'
836 cmd = ['-c', 'user.name="%s"' % safe_str(user_name),
850 cmd = ['-c', 'user.name="%s"' % safe_str(user_name),
837 '-c', 'user.email=%s' % safe_str(user_email),
851 '-c', 'user.email=%s' % safe_str(user_email),
838 'merge', '--no-ff', '-m', safe_str(merge_message)]
852 'merge', '--no-ff', '-m', safe_str(merge_message)]
839 cmd.extend(heads)
853
854 merge_cmd = cmd + heads
855
840 try:
856 try:
841 output = self.run_git_command(cmd, fail_on_stderr=False)
857 self.run_git_command(merge_cmd, fail_on_stderr=False)
842 except RepositoryError:
858 except RepositoryError:
843 files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'],
859 files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'],
844 fail_on_stderr=False)[0].splitlines()
860 fail_on_stderr=False)[0].splitlines()
@@ -846,6 +862,7 b' class GitRepository(BaseRepository):'
846 unresolved = ['U {}'.format(f) for f in files]
862 unresolved = ['U {}'.format(f) for f in files]
847
863
848 # Cleanup any merge leftovers
864 # Cleanup any merge leftovers
865 self._remote.invalidate_vcs_cache()
849 self.run_git_command(['merge', '--abort'], fail_on_stderr=False)
866 self.run_git_command(['merge', '--abort'], fail_on_stderr=False)
850
867
851 if unresolved:
868 if unresolved:
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -297,13 +297,20 b' class MercurialRepository(BaseRepository'
297 return update_cache
297 return update_cache
298
298
299 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
299 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
300 log.debug('Calculating common ancestor between %sc1:%s and %sc2:%s',
301 self, commit_id1, repo2, commit_id2)
302
300 if commit_id1 == commit_id2:
303 if commit_id1 == commit_id2:
301 return commit_id1
304 return commit_id1
302
305
303 ancestors = self._remote.revs_from_revspec(
306 ancestors = self._remote.revs_from_revspec(
304 "ancestor(id(%s), id(%s))", commit_id1, commit_id2,
307 "ancestor(id(%s), id(%s))", commit_id1, commit_id2,
305 other_path=repo2.path)
308 other_path=repo2.path)
306 return repo2[ancestors[0]].raw_id if ancestors else None
309
310 ancestor_id = repo2[ancestors[0]].raw_id if ancestors else None
311
312 log.debug('Found common ancestor with sha: %s', ancestor_id)
313 return ancestor_id
307
314
308 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
315 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
309 if commit_id1 == commit_id2:
316 if commit_id1 == commit_id2:
@@ -429,7 +436,8 b' class MercurialRepository(BaseRepository'
429 """
436 """
430 return os.path.join(self.path, '.hg', '.hgrc')
437 return os.path.join(self.path, '.hg', '.hgrc')
431
438
432 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None):
439 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
440 translate_tag=None, maybe_unreachable=False):
433 """
441 """
434 Returns ``MercurialCommit`` object representing repository's
442 Returns ``MercurialCommit`` object representing repository's
435 commit at the given `commit_id` or `commit_idx`.
443 commit at the given `commit_id` or `commit_idx`.
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -173,11 +173,10 b' class SubversionCommit(base.BaseCommit):'
173 raise CommitError(
173 raise CommitError(
174 "Directory does not exist for commit %s at "
174 "Directory does not exist for commit %s at "
175 " '%s'" % (self.raw_id, path))
175 " '%s'" % (self.raw_id, path))
176 path = self._fix_path(path)
176 path = safe_str(self._fix_path(path))
177
177
178 path_nodes = []
178 path_nodes = []
179 for name, kind in self._remote.get_nodes(
179 for name, kind in self._remote.get_nodes(path, revision=self._svn_rev):
180 safe_str(path), revision=self._svn_rev):
181 node_path = vcspath.join(path, name)
180 node_path = vcspath.join(path, name)
182 if kind == 'dir':
181 if kind == 'dir':
183 node = nodes.DirNode(node_path, commit=self)
182 node = nodes.DirNode(node_path, commit=self)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -276,7 +276,8 b' class SubversionRepository(base.BaseRepo'
276 """
276 """
277 return os.path.join(self.path, 'hooks')
277 return os.path.join(self.path, 'hooks')
278
278
279 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None):
279 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
280 translate_tag=None, maybe_unreachable=False):
280 if self.is_empty():
281 if self.is_empty():
281 raise EmptyRepositoryError("There are no commits yet")
282 raise EmptyRepositoryError("There are no commits yet")
282 if commit_id is not None:
283 if commit_id is not None:
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2019 RhodeCode GmbH
3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -30,7 +30,7 b' from pyramid.threadlocal import get_curr'
30 from sqlalchemy.sql.expression import null
30 from sqlalchemy.sql.expression import null
31 from sqlalchemy.sql.functions import coalesce
31 from sqlalchemy.sql.functions import coalesce
32
32
33 from rhodecode.lib import helpers as h, diffs, channelstream
33 from rhodecode.lib import helpers as h, diffs, channelstream, hooks_utils
34 from rhodecode.lib import audit_logger
34 from rhodecode.lib import audit_logger
35 from rhodecode.lib.utils2 import extract_mentioned_users, safe_str
35 from rhodecode.lib.utils2 import extract_mentioned_users, safe_str
36 from rhodecode.model import BaseModel
36 from rhodecode.model import BaseModel
@@ -720,6 +720,26 b' class CommentsModel(BaseModel):'
720 settings = settings_model.get_general_settings()
720 settings = settings_model.get_general_settings()
721 return settings.get('rhodecode_use_outdated_comments', False)
721 return settings.get('rhodecode_use_outdated_comments', False)
722
722
723 def trigger_commit_comment_hook(self, repo, user, action, data=None):
724 repo = self._get_repo(repo)
725 target_scm = repo.scm_instance()
726 if action == 'create':
727 trigger_hook = hooks_utils.trigger_comment_commit_hooks
728 elif action == 'edit':
729 # TODO(dan): when this is supported we trigger edit hook too
730 return
731 else:
732 return
733
734 log.debug('Handling repo %s trigger_commit_comment_hook with action %s: %s',
735 repo, action, trigger_hook)
736 trigger_hook(
737 username=user.username,
738 repo_name=repo.repo_name,
739 repo_type=target_scm.alias,
740 repo=repo,
741 data=data)
742
723
743
724 def _parse_comment_line_number(line_no):
744 def _parse_comment_line_number(line_no):
725 """
745 """
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -617,6 +617,7 b' class User(Base, BaseModel):'
617 user_gists = relationship('Gist', cascade='all')
617 user_gists = relationship('Gist', cascade='all')
618 # user pull requests
618 # user pull requests
619 user_pull_requests = relationship('PullRequest', cascade='all')
619 user_pull_requests = relationship('PullRequest', cascade='all')
620
620 # external identities
621 # external identities
621 external_identities = relationship(
622 external_identities = relationship(
622 'ExternalIdentity',
623 'ExternalIdentity',
@@ -1048,6 +1049,11 b' class User(Base, BaseModel):'
1048 Session().refresh(user)
1049 Session().refresh(user)
1049 return user
1050 return user
1050
1051
1052 @classmethod
1053 def get_default_user_id(cls):
1054 import rhodecode
1055 return rhodecode.CONFIG['default_user_id']
1056
1051 def _get_default_perms(self, user, suffix=''):
1057 def _get_default_perms(self, user, suffix=''):
1052 from rhodecode.model.permission import PermissionModel
1058 from rhodecode.model.permission import PermissionModel
1053 return PermissionModel().get_default_perms(user.user_perms, suffix)
1059 return PermissionModel().get_default_perms(user.user_perms, suffix)
@@ -2339,9 +2345,10 b' class Repository(Base, BaseModel):'
2339 # SCM PROPERTIES
2345 # SCM PROPERTIES
2340 #==========================================================================
2346 #==========================================================================
2341
2347
2342 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
2348 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, maybe_unreachable=False):
2343 return get_commit_safe(
2349 return get_commit_safe(
2344 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load)
2350 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load,
2351 maybe_unreachable=maybe_unreachable)
2345
2352
2346 def get_changeset(self, rev=None, pre_load=None):
2353 def get_changeset(self, rev=None, pre_load=None):
2347 warnings.warn("Use get_commit", DeprecationWarning)
2354 warnings.warn("Use get_commit", DeprecationWarning)
@@ -3710,6 +3717,9 b' class ChangesetComment(Base, BaseModel):'
3710 COMMENT_TYPE_TODO = u'todo'
3717 COMMENT_TYPE_TODO = u'todo'
3711 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3718 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3712
3719
3720 OP_IMMUTABLE = u'immutable'
3721 OP_CHANGEABLE = u'changeable'
3722
3713 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3723 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3714 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3724 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3715 revision = Column('revision', String(40), nullable=True)
3725 revision = Column('revision', String(40), nullable=True)
@@ -3724,6 +3734,7 b' class ChangesetComment(Base, BaseModel):'
3724 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3734 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3725 renderer = Column('renderer', Unicode(64), nullable=True)
3735 renderer = Column('renderer', Unicode(64), nullable=True)
3726 display_state = Column('display_state', Unicode(128), nullable=True)
3736 display_state = Column('display_state', Unicode(128), nullable=True)
3737 immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE)
3727
3738
3728 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3739 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3729 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
3740 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
@@ -3766,6 +3777,10 b' class ChangesetComment(Base, BaseModel):'
3766 def outdated(self):
3777 def outdated(self):
3767 return self.display_state == self.COMMENT_OUTDATED
3778 return self.display_state == self.COMMENT_OUTDATED
3768
3779
3780 @property
3781 def immutable(self):
3782 return self.immutable_state == self.OP_IMMUTABLE
3783
3769 def outdated_at_version(self, version):
3784 def outdated_at_version(self, version):
3770 """
3785 """
3771 Checks if comment is outdated for given pull request version
3786 Checks if comment is outdated for given pull request version
@@ -3814,7 +3829,9 b' class ChangesetComment(Base, BaseModel):'
3814 'comment_lineno': comment.line_no,
3829 'comment_lineno': comment.line_no,
3815 'comment_author': comment.author,
3830 'comment_author': comment.author,
3816 'comment_created_on': comment.created_on,
3831 'comment_created_on': comment.created_on,
3817 'comment_resolved_by': self.resolved
3832 'comment_resolved_by': self.resolved,
3833 'comment_commit_id': comment.revision,
3834 'comment_pull_request_id': comment.pull_request_id,
3818 }
3835 }
3819 return data
3836 return data
3820
3837
@@ -3973,6 +3990,8 b' class _PullRequestBase(BaseModel):'
3973 _revisions = Column(
3990 _revisions = Column(
3974 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
3991 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
3975
3992
3993 common_ancestor_id = Column('common_ancestor_id', Unicode(255), nullable=True)
3994
3976 @declared_attr
3995 @declared_attr
3977 def source_repo_id(cls):
3996 def source_repo_id(cls):
3978 # TODO: dan: rename column to source_repo_id
3997 # TODO: dan: rename column to source_repo_id
@@ -4024,6 +4043,10 b' class _PullRequestBase(BaseModel):'
4024 _last_merge_target_rev = Column(
4043 _last_merge_target_rev = Column(
4025 'last_merge_other_rev', String(40), nullable=True)
4044 'last_merge_other_rev', String(40), nullable=True)
4026 _last_merge_status = Column('merge_status', Integer(), nullable=True)
4045 _last_merge_status = Column('merge_status', Integer(), nullable=True)
4046 last_merge_metadata = Column(
4047 'last_merge_metadata', MutationObj.as_mutable(
4048 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4049
4027 merge_rev = Column('merge_rev', String(40), nullable=True)
4050 merge_rev = Column('merge_rev', String(40), nullable=True)
4028
4051
4029 reviewer_data = Column(
4052 reviewer_data = Column(
@@ -4123,10 +4146,11 b' class _PullRequestBase(BaseModel):'
4123
4146
4124 pull_request = self
4147 pull_request = self
4125 if with_merge_state:
4148 if with_merge_state:
4126 merge_status = PullRequestModel().merge_status(pull_request)
4149 merge_response, merge_status, msg = \
4150 PullRequestModel().merge_status(pull_request)
4127 merge_state = {
4151 merge_state = {
4128 'status': merge_status[0],
4152 'status': merge_status,
4129 'message': safe_unicode(merge_status[1]),
4153 'message': safe_unicode(msg),
4130 }
4154 }
4131 else:
4155 else:
4132 merge_state = {'status': 'not_available',
4156 merge_state = {'status': 'not_available',
@@ -4281,7 +4305,7 b' class PullRequest(Base, _PullRequestBase'
4281 attrs.source_ref_parts = pull_request_obj.source_ref_parts
4305 attrs.source_ref_parts = pull_request_obj.source_ref_parts
4282 attrs.target_ref_parts = pull_request_obj.target_ref_parts
4306 attrs.target_ref_parts = pull_request_obj.target_ref_parts
4283 attrs.revisions = pull_request_obj.revisions
4307 attrs.revisions = pull_request_obj.revisions
4284
4308 attrs.common_ancestor_id = pull_request_obj.common_ancestor_id
4285 attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref
4309 attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref
4286 attrs.reviewer_data = org_pull_request_obj.reviewer_data
4310 attrs.reviewer_data = org_pull_request_obj.reviewer_data
4287 attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json
4311 attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json
@@ -4511,6 +4535,65 b' class UserNotification(Base, BaseModel):'
4511 Session().add(self)
4535 Session().add(self)
4512
4536
4513
4537
4538 class UserNotice(Base, BaseModel):
4539 __tablename__ = 'user_notices'
4540 __table_args__ = (
4541 base_table_args
4542 )
4543
4544 NOTIFICATION_TYPE_MESSAGE = 'message'
4545 NOTIFICATION_TYPE_NOTICE = 'notice'
4546
4547 NOTIFICATION_LEVEL_INFO = 'info'
4548 NOTIFICATION_LEVEL_WARNING = 'warning'
4549 NOTIFICATION_LEVEL_ERROR = 'error'
4550
4551 user_notice_id = Column('gist_id', Integer(), primary_key=True)
4552
4553 notice_subject = Column('notice_subject', Unicode(512), nullable=True)
4554 notice_body = Column('notice_body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
4555
4556 notice_read = Column('notice_read', Boolean, default=False)
4557
4558 notification_level = Column('notification_level', String(1024), default=NOTIFICATION_LEVEL_INFO)
4559 notification_type = Column('notification_type', String(1024), default=NOTIFICATION_TYPE_NOTICE)
4560
4561 notice_created_by = Column('notice_created_by', Integer(), ForeignKey('users.user_id'), nullable=True)
4562 notice_created_on = Column('notice_created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4563
4564 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'))
4565 user = relationship('User', lazy="joined", primaryjoin='User.user_id==UserNotice.user_id')
4566
4567 @classmethod
4568 def create_for_user(cls, user, subject, body, notice_level=NOTIFICATION_LEVEL_INFO, allow_duplicate=False):
4569
4570 if notice_level not in [cls.NOTIFICATION_LEVEL_ERROR,
4571 cls.NOTIFICATION_LEVEL_WARNING,
4572 cls.NOTIFICATION_LEVEL_INFO]:
4573 return
4574
4575 from rhodecode.model.user import UserModel
4576 user = UserModel().get_user(user)
4577
4578 new_notice = UserNotice()
4579 if not allow_duplicate:
4580 existing_msg = UserNotice().query() \
4581 .filter(UserNotice.user == user) \
4582 .filter(UserNotice.notice_body == body) \
4583 .filter(UserNotice.notice_read == false()) \
4584 .scalar()
4585 if existing_msg:
4586 log.warning('Ignoring duplicate notice for user %s', user)
4587 return
4588
4589 new_notice.user = user
4590 new_notice.notice_subject = subject
4591 new_notice.notice_body = body
4592 new_notice.notification_level = notice_level
4593 Session().add(new_notice)
4594 Session().commit()
4595
4596
4514 class Gist(Base, BaseModel):
4597 class Gist(Base, BaseModel):
4515 __tablename__ = 'gists'
4598 __tablename__ = 'gists'
4516 __table_args__ = (
4599 __table_args__ = (
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,6 +26,7 b' Model for notifications'
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 import premailer
29 from pyramid.threadlocal import get_current_request
30 from pyramid.threadlocal import get_current_request
30 from sqlalchemy.sql.expression import false, true
31 from sqlalchemy.sql.expression import false, true
31
32
@@ -303,6 +304,7 b' class EmailNotificationModel(BaseModel):'
303 TYPE_PASSWORD_RESET = 'password_reset'
304 TYPE_PASSWORD_RESET = 'password_reset'
304 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
305 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
305 TYPE_EMAIL_TEST = 'email_test'
306 TYPE_EMAIL_TEST = 'email_test'
307 TYPE_EMAIL_EXCEPTION = 'exception'
306 TYPE_TEST = 'test'
308 TYPE_TEST = 'test'
307
309
308 email_types = {
310 email_types = {
@@ -310,6 +312,8 b' class EmailNotificationModel(BaseModel):'
310 'rhodecode:templates/email_templates/main.mako',
312 'rhodecode:templates/email_templates/main.mako',
311 TYPE_TEST:
313 TYPE_TEST:
312 'rhodecode:templates/email_templates/test.mako',
314 'rhodecode:templates/email_templates/test.mako',
315 TYPE_EMAIL_EXCEPTION:
316 'rhodecode:templates/email_templates/exception_tracker.mako',
313 TYPE_EMAIL_TEST:
317 TYPE_EMAIL_TEST:
314 'rhodecode:templates/email_templates/email_test.mako',
318 'rhodecode:templates/email_templates/email_test.mako',
315 TYPE_REGISTRATION:
319 TYPE_REGISTRATION:
@@ -328,6 +332,12 b' class EmailNotificationModel(BaseModel):'
328 'rhodecode:templates/email_templates/pull_request_update.mako',
332 'rhodecode:templates/email_templates/pull_request_update.mako',
329 }
333 }
330
334
335 premailer_instance = premailer.Premailer(
336 cssutils_logging_level=logging.WARNING,
337 cssutils_logging_handler=logging.getLogger().handlers[0]
338 if logging.getLogger().handlers else None,
339 )
340
331 def __init__(self):
341 def __init__(self):
332 """
342 """
333 Example usage::
343 Example usage::
@@ -391,4 +401,11 b' class EmailNotificationModel(BaseModel):'
391 # render WHOLE template
401 # render WHOLE template
392 body = email_template.render(None, **_kwargs)
402 body = email_template.render(None, **_kwargs)
393
403
404 try:
405 # Inline CSS styles and conversion
406 body = self.premailer_instance.transform(body)
407 except Exception:
408 log.exception('Failed to parse body with premailer')
409 pass
410
394 return subject, headers, body, body_plaintext
411 return subject, headers, body, body_plaintext
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -558,7 +558,7 b' class PermissionModel(BaseModel):'
558
558
559 def get_users_with_repo_write(self, db_repo):
559 def get_users_with_repo_write(self, db_repo):
560 write_plus = ['repository.write', 'repository.admin']
560 write_plus = ['repository.write', 'repository.admin']
561 default_user_id = User.get_default_user().user_id
561 default_user_id = User.get_default_user_id()
562 user_write_permissions = collections.OrderedDict()
562 user_write_permissions = collections.OrderedDict()
563
563
564 # write+ and DEFAULT user for inheritance
564 # write+ and DEFAULT user for inheritance
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -35,7 +35,7 b' import collections'
35 from pyramid import compat
35 from pyramid import compat
36 from pyramid.threadlocal import get_current_request
36 from pyramid.threadlocal import get_current_request
37
37
38 from rhodecode import events
38 from rhodecode.lib.vcs.nodes import FileNode
39 from rhodecode.translation import lazy_ugettext
39 from rhodecode.translation import lazy_ugettext
40 from rhodecode.lib import helpers as h, hooks_utils, diffs
40 from rhodecode.lib import helpers as h, hooks_utils, diffs
41 from rhodecode.lib import audit_logger
41 from rhodecode.lib import audit_logger
@@ -43,9 +43,12 b' from rhodecode.lib.compat import Ordered'
43 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
43 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
44 from rhodecode.lib.markup_renderer import (
44 from rhodecode.lib.markup_renderer import (
45 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
45 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
46 from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe
46 from rhodecode.lib.utils2 import (
47 safe_unicode, safe_str, md5_safe, AttributeDict, safe_int,
48 get_current_rhodecode_user)
47 from rhodecode.lib.vcs.backends.base import (
49 from rhodecode.lib.vcs.backends.base import (
48 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason)
50 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason,
51 TargetRefMissing, SourceRefMissing)
49 from rhodecode.lib.vcs.conf import settings as vcs_settings
52 from rhodecode.lib.vcs.conf import settings as vcs_settings
50 from rhodecode.lib.vcs.exceptions import (
53 from rhodecode.lib.vcs.exceptions import (
51 CommitDoesNotExistError, EmptyRepositoryError)
54 CommitDoesNotExistError, EmptyRepositoryError)
@@ -54,7 +57,7 b' from rhodecode.model.changeset_status im'
54 from rhodecode.model.comment import CommentsModel
57 from rhodecode.model.comment import CommentsModel
55 from rhodecode.model.db import (
58 from rhodecode.model.db import (
56 or_, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
59 or_, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
57 PullRequestVersion, ChangesetComment, Repository, RepoReviewRule)
60 PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User)
58 from rhodecode.model.meta import Session
61 from rhodecode.model.meta import Session
59 from rhodecode.model.notification import NotificationModel, \
62 from rhodecode.model.notification import NotificationModel, \
60 EmailNotificationModel
63 EmailNotificationModel
@@ -82,6 +85,126 b' class UpdateResponse(object):'
82 self.target_changed = target_changed
85 self.target_changed = target_changed
83
86
84
87
88 def get_diff_info(
89 source_repo, source_ref, target_repo, target_ref, get_authors=False,
90 get_commit_authors=True):
91 """
92 Calculates detailed diff information for usage in preview of creation of a pull-request.
93 This is also used for default reviewers logic
94 """
95
96 source_scm = source_repo.scm_instance()
97 target_scm = target_repo.scm_instance()
98
99 ancestor_id = target_scm.get_common_ancestor(target_ref, source_ref, source_scm)
100 if not ancestor_id:
101 raise ValueError(
102 'cannot calculate diff info without a common ancestor. '
103 'Make sure both repositories are related, and have a common forking commit.')
104
105 # case here is that want a simple diff without incoming commits,
106 # previewing what will be merged based only on commits in the source.
107 log.debug('Using ancestor %s as source_ref instead of %s',
108 ancestor_id, source_ref)
109
110 # source of changes now is the common ancestor
111 source_commit = source_scm.get_commit(commit_id=ancestor_id)
112 # target commit becomes the source ref as it is the last commit
113 # for diff generation this logic gives proper diff
114 target_commit = source_scm.get_commit(commit_id=source_ref)
115
116 vcs_diff = \
117 source_scm.get_diff(commit1=source_commit, commit2=target_commit,
118 ignore_whitespace=False, context=3)
119
120 diff_processor = diffs.DiffProcessor(
121 vcs_diff, format='newdiff', diff_limit=None,
122 file_limit=None, show_full_diff=True)
123
124 _parsed = diff_processor.prepare()
125
126 all_files = []
127 all_files_changes = []
128 changed_lines = {}
129 stats = [0, 0]
130 for f in _parsed:
131 all_files.append(f['filename'])
132 all_files_changes.append({
133 'filename': f['filename'],
134 'stats': f['stats']
135 })
136 stats[0] += f['stats']['added']
137 stats[1] += f['stats']['deleted']
138
139 changed_lines[f['filename']] = []
140 if len(f['chunks']) < 2:
141 continue
142 # first line is "context" information
143 for chunks in f['chunks'][1:]:
144 for chunk in chunks['lines']:
145 if chunk['action'] not in ('del', 'mod'):
146 continue
147 changed_lines[f['filename']].append(chunk['old_lineno'])
148
149 commit_authors = []
150 user_counts = {}
151 email_counts = {}
152 author_counts = {}
153 _commit_cache = {}
154
155 commits = []
156 if get_commit_authors:
157 commits = target_scm.compare(
158 target_ref, source_ref, source_scm, merge=True,
159 pre_load=["author"])
160
161 for commit in commits:
162 user = User.get_from_cs_author(commit.author)
163 if user and user not in commit_authors:
164 commit_authors.append(user)
165
166 # lines
167 if get_authors:
168 target_commit = source_repo.get_commit(ancestor_id)
169
170 for fname, lines in changed_lines.items():
171 try:
172 node = target_commit.get_node(fname)
173 except Exception:
174 continue
175
176 if not isinstance(node, FileNode):
177 continue
178
179 for annotation in node.annotate:
180 line_no, commit_id, get_commit_func, line_text = annotation
181 if line_no in lines:
182 if commit_id not in _commit_cache:
183 _commit_cache[commit_id] = get_commit_func()
184 commit = _commit_cache[commit_id]
185 author = commit.author
186 email = commit.author_email
187 user = User.get_from_cs_author(author)
188 if user:
189 user_counts[user] = user_counts.get(user, 0) + 1
190 author_counts[author] = author_counts.get(author, 0) + 1
191 email_counts[email] = email_counts.get(email, 0) + 1
192
193 return {
194 'commits': commits,
195 'files': all_files_changes,
196 'stats': stats,
197 'ancestor': ancestor_id,
198 # original authors of modified files
199 'original_authors': {
200 'users': user_counts,
201 'authors': author_counts,
202 'emails': email_counts,
203 },
204 'commit_authors': commit_authors
205 }
206
207
85 class PullRequestModel(BaseModel):
208 class PullRequestModel(BaseModel):
86
209
87 cls = PullRequest
210 cls = PullRequest
@@ -160,8 +283,10 b' class PullRequestModel(BaseModel):'
160
283
161 if search_q:
284 if search_q:
162 like_expression = u'%{}%'.format(safe_unicode(search_q))
285 like_expression = u'%{}%'.format(safe_unicode(search_q))
286 q = q.join(User)
163 q = q.filter(or_(
287 q = q.filter(or_(
164 cast(PullRequest.pull_request_id, String).ilike(like_expression),
288 cast(PullRequest.pull_request_id, String).ilike(like_expression),
289 User.username.ilike(like_expression),
165 PullRequest.title.ilike(like_expression),
290 PullRequest.title.ilike(like_expression),
166 PullRequest.description.ilike(like_expression),
291 PullRequest.description.ilike(like_expression),
167 ))
292 ))
@@ -355,7 +480,7 b' class PullRequestModel(BaseModel):'
355 PullRequestReviewers.user_id == user_id).all()
480 PullRequestReviewers.user_id == user_id).all()
356 ]
481 ]
357
482
358 def _prepare_participating_query(self, user_id=None, statuses=None,
483 def _prepare_participating_query(self, user_id=None, statuses=None, query='',
359 order_by=None, order_dir='desc'):
484 order_by=None, order_dir='desc'):
360 q = PullRequest.query()
485 q = PullRequest.query()
361 if user_id:
486 if user_id:
@@ -372,6 +497,15 b' class PullRequestModel(BaseModel):'
372 if statuses:
497 if statuses:
373 q = q.filter(PullRequest.status.in_(statuses))
498 q = q.filter(PullRequest.status.in_(statuses))
374
499
500 if query:
501 like_expression = u'%{}%'.format(safe_unicode(query))
502 q = q.join(User)
503 q = q.filter(or_(
504 cast(PullRequest.pull_request_id, String).ilike(like_expression),
505 User.username.ilike(like_expression),
506 PullRequest.title.ilike(like_expression),
507 PullRequest.description.ilike(like_expression),
508 ))
375 if order_by:
509 if order_by:
376 order_map = {
510 order_map = {
377 'name_raw': PullRequest.pull_request_id,
511 'name_raw': PullRequest.pull_request_id,
@@ -386,19 +520,19 b' class PullRequestModel(BaseModel):'
386
520
387 return q
521 return q
388
522
389 def count_im_participating_in(self, user_id=None, statuses=None):
523 def count_im_participating_in(self, user_id=None, statuses=None, query=''):
390 q = self._prepare_participating_query(user_id, statuses=statuses)
524 q = self._prepare_participating_query(user_id, statuses=statuses, query=query)
391 return q.count()
525 return q.count()
392
526
393 def get_im_participating_in(
527 def get_im_participating_in(
394 self, user_id=None, statuses=None, offset=0,
528 self, user_id=None, statuses=None, query='', offset=0,
395 length=None, order_by=None, order_dir='desc'):
529 length=None, order_by=None, order_dir='desc'):
396 """
530 """
397 Get all Pull requests that i'm participating in, or i have opened
531 Get all Pull requests that i'm participating in, or i have opened
398 """
532 """
399
533
400 q = self._prepare_participating_query(
534 q = self._prepare_participating_query(
401 user_id, statuses=statuses, order_by=order_by,
535 user_id, statuses=statuses, query=query, order_by=order_by,
402 order_dir=order_dir)
536 order_dir=order_dir)
403
537
404 if length:
538 if length:
@@ -442,6 +576,7 b' class PullRequestModel(BaseModel):'
442
576
443 def create(self, created_by, source_repo, source_ref, target_repo,
577 def create(self, created_by, source_repo, source_ref, target_repo,
444 target_ref, revisions, reviewers, title, description=None,
578 target_ref, revisions, reviewers, title, description=None,
579 common_ancestor_id=None,
445 description_renderer=None,
580 description_renderer=None,
446 reviewer_data=None, translator=None, auth_user=None):
581 reviewer_data=None, translator=None, auth_user=None):
447 translator = translator or get_current_request().translate
582 translator = translator or get_current_request().translate
@@ -463,6 +598,8 b' class PullRequestModel(BaseModel):'
463 pull_request.author = created_by_user
598 pull_request.author = created_by_user
464 pull_request.reviewer_data = reviewer_data
599 pull_request.reviewer_data = reviewer_data
465 pull_request.pull_request_state = pull_request.STATE_CREATING
600 pull_request.pull_request_state = pull_request.STATE_CREATING
601 pull_request.common_ancestor_id = common_ancestor_id
602
466 Session().add(pull_request)
603 Session().add(pull_request)
467 Session().flush()
604 Session().flush()
468
605
@@ -542,8 +679,7 b' class PullRequestModel(BaseModel):'
542 pull_request, auth_user=auth_user, translator=translator)
679 pull_request, auth_user=auth_user, translator=translator)
543
680
544 self.notify_reviewers(pull_request, reviewer_ids)
681 self.notify_reviewers(pull_request, reviewer_ids)
545 self.trigger_pull_request_hook(
682 self.trigger_pull_request_hook(pull_request, created_by_user, 'create')
546 pull_request, created_by_user, 'create')
547
683
548 creation_data = pull_request.get_api_data(with_merge_state=False)
684 creation_data = pull_request.get_api_data(with_merge_state=False)
549 self._log_audit_action(
685 self._log_audit_action(
@@ -556,28 +692,26 b' class PullRequestModel(BaseModel):'
556 pull_request = self.__get_pull_request(pull_request)
692 pull_request = self.__get_pull_request(pull_request)
557 target_scm = pull_request.target_repo.scm_instance()
693 target_scm = pull_request.target_repo.scm_instance()
558 if action == 'create':
694 if action == 'create':
559 trigger_hook = hooks_utils.trigger_log_create_pull_request_hook
695 trigger_hook = hooks_utils.trigger_create_pull_request_hook
560 elif action == 'merge':
696 elif action == 'merge':
561 trigger_hook = hooks_utils.trigger_log_merge_pull_request_hook
697 trigger_hook = hooks_utils.trigger_merge_pull_request_hook
562 elif action == 'close':
698 elif action == 'close':
563 trigger_hook = hooks_utils.trigger_log_close_pull_request_hook
699 trigger_hook = hooks_utils.trigger_close_pull_request_hook
564 elif action == 'review_status_change':
700 elif action == 'review_status_change':
565 trigger_hook = hooks_utils.trigger_log_review_pull_request_hook
701 trigger_hook = hooks_utils.trigger_review_pull_request_hook
566 elif action == 'update':
702 elif action == 'update':
567 trigger_hook = hooks_utils.trigger_log_update_pull_request_hook
703 trigger_hook = hooks_utils.trigger_update_pull_request_hook
568 elif action == 'comment':
704 elif action == 'comment':
569 # dummy hook ! for comment. We want this function to handle all cases
705 trigger_hook = hooks_utils.trigger_comment_pull_request_hook
570 def trigger_hook(*args, **kwargs):
571 pass
572 comment = data['comment']
573 events.trigger(events.PullRequestCommentEvent(pull_request, comment))
574 else:
706 else:
575 return
707 return
576
708
709 log.debug('Handling pull_request %s trigger_pull_request_hook with action %s and hook: %s',
710 pull_request, action, trigger_hook)
577 trigger_hook(
711 trigger_hook(
578 username=user.username,
712 username=user.username,
579 repo_name=pull_request.target_repo.repo_name,
713 repo_name=pull_request.target_repo.repo_name,
580 repo_alias=target_scm.alias,
714 repo_type=target_scm.alias,
581 pull_request=pull_request,
715 pull_request=pull_request,
582 data=data)
716 data=data)
583
717
@@ -684,6 +818,38 b' class PullRequestModel(BaseModel):'
684 source_ref_type = pull_request.source_ref_parts.type
818 source_ref_type = pull_request.source_ref_parts.type
685 return source_ref_type in self.REF_TYPES
819 return source_ref_type in self.REF_TYPES
686
820
821 def get_flow_commits(self, pull_request):
822
823 # source repo
824 source_ref_name = pull_request.source_ref_parts.name
825 source_ref_type = pull_request.source_ref_parts.type
826 source_ref_id = pull_request.source_ref_parts.commit_id
827 source_repo = pull_request.source_repo.scm_instance()
828
829 try:
830 if source_ref_type in self.REF_TYPES:
831 source_commit = source_repo.get_commit(source_ref_name)
832 else:
833 source_commit = source_repo.get_commit(source_ref_id)
834 except CommitDoesNotExistError:
835 raise SourceRefMissing()
836
837 # target repo
838 target_ref_name = pull_request.target_ref_parts.name
839 target_ref_type = pull_request.target_ref_parts.type
840 target_ref_id = pull_request.target_ref_parts.commit_id
841 target_repo = pull_request.target_repo.scm_instance()
842
843 try:
844 if target_ref_type in self.REF_TYPES:
845 target_commit = target_repo.get_commit(target_ref_name)
846 else:
847 target_commit = target_repo.get_commit(target_ref_id)
848 except CommitDoesNotExistError:
849 raise TargetRefMissing()
850
851 return source_commit, target_commit
852
687 def update_commits(self, pull_request, updating_user):
853 def update_commits(self, pull_request, updating_user):
688 """
854 """
689 Get the updated list of commits for the pull request
855 Get the updated list of commits for the pull request
@@ -710,31 +876,22 b' class PullRequestModel(BaseModel):'
710 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
876 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
711 source_changed=False, target_changed=False)
877 source_changed=False, target_changed=False)
712
878
713 # source repo
714 source_repo = pull_request.source_repo.scm_instance()
715
716 try:
879 try:
717 source_commit = source_repo.get_commit(commit_id=source_ref_name)
880 source_commit, target_commit = self.get_flow_commits(pull_request)
718 except CommitDoesNotExistError:
881 except SourceRefMissing:
719 return UpdateResponse(
882 return UpdateResponse(
720 executed=False,
883 executed=False,
721 reason=UpdateFailureReason.MISSING_SOURCE_REF,
884 reason=UpdateFailureReason.MISSING_SOURCE_REF,
722 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
885 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
723 source_changed=False, target_changed=False)
886 source_changed=False, target_changed=False)
724
887 except TargetRefMissing:
725 source_changed = source_ref_id != source_commit.raw_id
726
727 # target repo
728 target_repo = pull_request.target_repo.scm_instance()
729
730 try:
731 target_commit = target_repo.get_commit(commit_id=target_ref_name)
732 except CommitDoesNotExistError:
733 return UpdateResponse(
888 return UpdateResponse(
734 executed=False,
889 executed=False,
735 reason=UpdateFailureReason.MISSING_TARGET_REF,
890 reason=UpdateFailureReason.MISSING_TARGET_REF,
736 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
891 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
737 source_changed=False, target_changed=False)
892 source_changed=False, target_changed=False)
893
894 source_changed = source_ref_id != source_commit.raw_id
738 target_changed = target_ref_id != target_commit.raw_id
895 target_changed = target_ref_id != target_commit.raw_id
739
896
740 if not (source_changed or target_changed):
897 if not (source_changed or target_changed):
@@ -764,17 +921,8 b' class PullRequestModel(BaseModel):'
764 ver.pull_request_version_id if ver else None
921 ver.pull_request_version_id if ver else None
765 pull_request_version = pull_request
922 pull_request_version = pull_request
766
923
767 try:
924 source_repo = pull_request.source_repo.scm_instance()
768 if target_ref_type in self.REF_TYPES:
925 target_repo = pull_request.target_repo.scm_instance()
769 target_commit = target_repo.get_commit(target_ref_name)
770 else:
771 target_commit = target_repo.get_commit(target_ref_id)
772 except CommitDoesNotExistError:
773 return UpdateResponse(
774 executed=False,
775 reason=UpdateFailureReason.MISSING_TARGET_REF,
776 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
777 source_changed=source_changed, target_changed=target_changed)
778
926
779 # re-compute commit ids
927 # re-compute commit ids
780 old_commit_ids = pull_request.revisions
928 old_commit_ids = pull_request.revisions
@@ -783,8 +931,17 b' class PullRequestModel(BaseModel):'
783 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
931 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
784 pre_load=pre_load)
932 pre_load=pre_load)
785
933
786 ancestor_commit_id = source_repo.get_common_ancestor(
934 target_ref = target_commit.raw_id
787 source_commit.raw_id, target_commit.raw_id, target_repo)
935 source_ref = source_commit.raw_id
936 ancestor_commit_id = target_repo.get_common_ancestor(
937 target_ref, source_ref, source_repo)
938
939 if not ancestor_commit_id:
940 raise ValueError(
941 'cannot calculate diff info without a common ancestor. '
942 'Make sure both repositories are related, and have a common forking commit.')
943
944 pull_request.common_ancestor_id = ancestor_commit_id
788
945
789 pull_request.source_ref = '%s:%s:%s' % (
946 pull_request.source_ref = '%s:%s:%s' % (
790 source_ref_type, source_ref_name, source_commit.raw_id)
947 source_ref_type, source_ref_name, source_commit.raw_id)
@@ -885,11 +1042,13 b' class PullRequestModel(BaseModel):'
885 version._last_merge_source_rev = pull_request._last_merge_source_rev
1042 version._last_merge_source_rev = pull_request._last_merge_source_rev
886 version._last_merge_target_rev = pull_request._last_merge_target_rev
1043 version._last_merge_target_rev = pull_request._last_merge_target_rev
887 version.last_merge_status = pull_request.last_merge_status
1044 version.last_merge_status = pull_request.last_merge_status
1045 version.last_merge_metadata = pull_request.last_merge_metadata
888 version.shadow_merge_ref = pull_request.shadow_merge_ref
1046 version.shadow_merge_ref = pull_request.shadow_merge_ref
889 version.merge_rev = pull_request.merge_rev
1047 version.merge_rev = pull_request.merge_rev
890 version.reviewer_data = pull_request.reviewer_data
1048 version.reviewer_data = pull_request.reviewer_data
891
1049
892 version.revisions = pull_request.revisions
1050 version.revisions = pull_request.revisions
1051 version.common_ancestor_id = pull_request.common_ancestor_id
893 version.pull_request = pull_request
1052 version.pull_request = pull_request
894 Session().add(version)
1053 Session().add(version)
895 Session().flush()
1054 Session().flush()
@@ -1270,7 +1429,10 b' class PullRequestModel(BaseModel):'
1270 email_kwargs=email_kwargs,
1429 email_kwargs=email_kwargs,
1271 )
1430 )
1272
1431
1273 def delete(self, pull_request, user):
1432 def delete(self, pull_request, user=None):
1433 if not user:
1434 user = getattr(get_current_rhodecode_user(), 'username', None)
1435
1274 pull_request = self.__get_pull_request(pull_request)
1436 pull_request = self.__get_pull_request(pull_request)
1275 old_data = pull_request.get_api_data(with_merge_state=False)
1437 old_data = pull_request.get_api_data(with_merge_state=False)
1276 self._cleanup_merge_workspace(pull_request)
1438 self._cleanup_merge_workspace(pull_request)
@@ -1285,8 +1447,7 b' class PullRequestModel(BaseModel):'
1285 pull_request.status = PullRequest.STATUS_CLOSED
1447 pull_request.status = PullRequest.STATUS_CLOSED
1286 pull_request.updated_on = datetime.datetime.now()
1448 pull_request.updated_on = datetime.datetime.now()
1287 Session().add(pull_request)
1449 Session().add(pull_request)
1288 self.trigger_pull_request_hook(
1450 self.trigger_pull_request_hook(pull_request, pull_request.author, 'close')
1289 pull_request, pull_request.author, 'close')
1290
1451
1291 pr_data = pull_request.get_api_data(with_merge_state=False)
1452 pr_data = pull_request.get_api_data(with_merge_state=False)
1292 self._log_audit_action(
1453 self._log_audit_action(
@@ -1332,47 +1493,46 b' class PullRequestModel(BaseModel):'
1332 )
1493 )
1333
1494
1334 Session().flush()
1495 Session().flush()
1335 events.trigger(events.PullRequestCommentEvent(pull_request, comment))
1496
1497 self.trigger_pull_request_hook(pull_request, user, 'comment',
1498 data={'comment': comment})
1499
1336 # we now calculate the status of pull request again, and based on that
1500 # we now calculate the status of pull request again, and based on that
1337 # calculation trigger status change. This might happen in cases
1501 # calculation trigger status change. This might happen in cases
1338 # that non-reviewer admin closes a pr, which means his vote doesn't
1502 # that non-reviewer admin closes a pr, which means his vote doesn't
1339 # change the status, while if he's a reviewer this might change it.
1503 # change the status, while if he's a reviewer this might change it.
1340 calculated_status = pull_request.calculated_review_status()
1504 calculated_status = pull_request.calculated_review_status()
1341 if old_calculated_status != calculated_status:
1505 if old_calculated_status != calculated_status:
1342 self.trigger_pull_request_hook(
1506 self.trigger_pull_request_hook(pull_request, user, 'review_status_change',
1343 pull_request, user, 'review_status_change',
1507 data={'status': calculated_status})
1344 data={'status': calculated_status})
1345
1508
1346 # finally close the PR
1509 # finally close the PR
1347 PullRequestModel().close_pull_request(
1510 PullRequestModel().close_pull_request(pull_request.pull_request_id, user)
1348 pull_request.pull_request_id, user)
1349
1511
1350 return comment, status
1512 return comment, status
1351
1513
1352 def merge_status(self, pull_request, translator=None,
1514 def merge_status(self, pull_request, translator=None, force_shadow_repo_refresh=False):
1353 force_shadow_repo_refresh=False):
1354 _ = translator or get_current_request().translate
1515 _ = translator or get_current_request().translate
1355
1516
1356 if not self._is_merge_enabled(pull_request):
1517 if not self._is_merge_enabled(pull_request):
1357 return False, _('Server-side pull request merging is disabled.')
1518 return None, False, _('Server-side pull request merging is disabled.')
1519
1358 if pull_request.is_closed():
1520 if pull_request.is_closed():
1359 return False, _('This pull request is closed.')
1521 return None, False, _('This pull request is closed.')
1522
1360 merge_possible, msg = self._check_repo_requirements(
1523 merge_possible, msg = self._check_repo_requirements(
1361 target=pull_request.target_repo, source=pull_request.source_repo,
1524 target=pull_request.target_repo, source=pull_request.source_repo,
1362 translator=_)
1525 translator=_)
1363 if not merge_possible:
1526 if not merge_possible:
1364 return merge_possible, msg
1527 return None, merge_possible, msg
1365
1528
1366 try:
1529 try:
1367 resp = self._try_merge(
1530 merge_response = self._try_merge(
1368 pull_request,
1531 pull_request, force_shadow_repo_refresh=force_shadow_repo_refresh)
1369 force_shadow_repo_refresh=force_shadow_repo_refresh)
1532 log.debug("Merge response: %s", merge_response)
1370 log.debug("Merge response: %s", resp)
1533 return merge_response, merge_response.possible, merge_response.merge_status_message
1371 status = resp.possible, resp.merge_status_message
1372 except NotImplementedError:
1534 except NotImplementedError:
1373 status = False, _('Pull request merging is not supported.')
1535 return None, False, _('Pull request merging is not supported.')
1374
1375 return status
1376
1536
1377 def _check_repo_requirements(self, target, source, translator):
1537 def _check_repo_requirements(self, target, source, translator):
1378 """
1538 """
@@ -1439,6 +1599,9 b' class PullRequestModel(BaseModel):'
1439 'target_ref': pull_request.target_ref_parts,
1599 'target_ref': pull_request.target_ref_parts,
1440 'source_ref': pull_request.source_ref_parts,
1600 'source_ref': pull_request.source_ref_parts,
1441 }
1601 }
1602 if pull_request.last_merge_metadata:
1603 metadata.update(pull_request.last_merge_metadata)
1604
1442 if not possible and target_ref.type == 'branch':
1605 if not possible and target_ref.type == 'branch':
1443 # NOTE(marcink): case for mercurial multiple heads on branch
1606 # NOTE(marcink): case for mercurial multiple heads on branch
1444 heads = target_vcs._heads(target_ref.name)
1607 heads = target_vcs._heads(target_ref.name)
@@ -1447,6 +1610,7 b' class PullRequestModel(BaseModel):'
1447 metadata.update({
1610 metadata.update({
1448 'heads': heads
1611 'heads': heads
1449 })
1612 })
1613
1450 merge_state = MergeResponse(
1614 merge_state = MergeResponse(
1451 possible, False, None, pull_request.last_merge_status, metadata=metadata)
1615 possible, False, None, pull_request.last_merge_status, metadata=metadata)
1452
1616
@@ -1487,6 +1651,8 b' class PullRequestModel(BaseModel):'
1487 pull_request.source_ref_parts.commit_id
1651 pull_request.source_ref_parts.commit_id
1488 pull_request._last_merge_target_rev = target_reference.commit_id
1652 pull_request._last_merge_target_rev = target_reference.commit_id
1489 pull_request.last_merge_status = merge_state.failure_reason
1653 pull_request.last_merge_status = merge_state.failure_reason
1654 pull_request.last_merge_metadata = merge_state.metadata
1655
1490 pull_request.shadow_merge_ref = merge_state.merge_ref
1656 pull_request.shadow_merge_ref = merge_state.merge_ref
1491 Session().add(pull_request)
1657 Session().add(pull_request)
1492 Session().commit()
1658 Session().commit()
@@ -1627,7 +1793,7 b' class PullRequestModel(BaseModel):'
1627 target_commit = source_repo.get_commit(
1793 target_commit = source_repo.get_commit(
1628 commit_id=safe_str(target_ref_id))
1794 commit_id=safe_str(target_ref_id))
1629 source_commit = source_repo.get_commit(
1795 source_commit = source_repo.get_commit(
1630 commit_id=safe_str(source_ref_id))
1796 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
1631 if isinstance(source_repo, Repository):
1797 if isinstance(source_repo, Repository):
1632 vcs_repo = source_repo.scm_instance()
1798 vcs_repo = source_repo.scm_instance()
1633 else:
1799 else:
@@ -1730,9 +1896,16 b' class MergeCheck(object):'
1730 self.review_status = None
1896 self.review_status = None
1731 self.merge_possible = None
1897 self.merge_possible = None
1732 self.merge_msg = ''
1898 self.merge_msg = ''
1899 self.merge_response = None
1733 self.failed = None
1900 self.failed = None
1734 self.errors = []
1901 self.errors = []
1735 self.error_details = OrderedDict()
1902 self.error_details = OrderedDict()
1903 self.source_commit = AttributeDict()
1904 self.target_commit = AttributeDict()
1905
1906 def __repr__(self):
1907 return '<MergeCheck(possible:{}, failed:{}, errors:{})>'.format(
1908 self.merge_possible, self.failed, self.errors)
1736
1909
1737 def push_error(self, error_type, message, error_key, details):
1910 def push_error(self, error_type, message, error_key, details):
1738 self.failed = True
1911 self.failed = True
@@ -1759,8 +1932,7 b' class MergeCheck(object):'
1759 return merge_check
1932 return merge_check
1760
1933
1761 # permissions to merge
1934 # permissions to merge
1762 user_allowed_to_merge = PullRequestModel().check_user_merge(
1935 user_allowed_to_merge = PullRequestModel().check_user_merge(pull_request, auth_user)
1763 pull_request, auth_user)
1764 if not user_allowed_to_merge:
1936 if not user_allowed_to_merge:
1765 log.debug("MergeCheck: cannot merge, approval is pending.")
1937 log.debug("MergeCheck: cannot merge, approval is pending.")
1766
1938
@@ -1822,11 +1994,31 b' class MergeCheck(object):'
1822 return merge_check
1994 return merge_check
1823
1995
1824 # merge possible, here is the filesystem simulation + shadow repo
1996 # merge possible, here is the filesystem simulation + shadow repo
1825 merge_status, msg = PullRequestModel().merge_status(
1997 merge_response, merge_status, msg = PullRequestModel().merge_status(
1826 pull_request, translator=translator,
1998 pull_request, translator=translator,
1827 force_shadow_repo_refresh=force_shadow_repo_refresh)
1999 force_shadow_repo_refresh=force_shadow_repo_refresh)
2000
1828 merge_check.merge_possible = merge_status
2001 merge_check.merge_possible = merge_status
1829 merge_check.merge_msg = msg
2002 merge_check.merge_msg = msg
2003 merge_check.merge_response = merge_response
2004
2005 source_ref_id = pull_request.source_ref_parts.commit_id
2006 target_ref_id = pull_request.target_ref_parts.commit_id
2007
2008 try:
2009 source_commit, target_commit = PullRequestModel().get_flow_commits(pull_request)
2010 merge_check.source_commit.changed = source_ref_id != source_commit.raw_id
2011 merge_check.source_commit.ref_spec = pull_request.source_ref_parts
2012 merge_check.source_commit.current_raw_id = source_commit.raw_id
2013 merge_check.source_commit.previous_raw_id = source_ref_id
2014
2015 merge_check.target_commit.changed = target_ref_id != target_commit.raw_id
2016 merge_check.target_commit.ref_spec = pull_request.target_ref_parts
2017 merge_check.target_commit.current_raw_id = target_commit.raw_id
2018 merge_check.target_commit.previous_raw_id = target_ref_id
2019 except (SourceRefMissing, TargetRefMissing):
2020 pass
2021
1830 if not merge_status:
2022 if not merge_status:
1831 log.debug("MergeCheck: cannot merge, pull request merge not possible.")
2023 log.debug("MergeCheck: cannot merge, pull request merge not possible.")
1832 merge_check.push_error('warning', msg, cls.MERGE_CHECK, None)
2024 merge_check.push_error('warning', msg, cls.MERGE_CHECK, None)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1098,7 +1098,7 b' class ReadmeFinder:'
1098 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1098 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1099 default_renderer, [])
1099 default_renderer, [])
1100
1100
1101 def search(self, commit, path='/'):
1101 def search(self, commit, path=u'/'):
1102 """
1102 """
1103 Find a readme in the given `commit`.
1103 Find a readme in the given `commit`.
1104 """
1104 """
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -473,7 +473,7 b' class ScmModel(BaseModel):'
473 # We trigger the post-push action
473 # We trigger the post-push action
474 hooks_utils.trigger_post_push_hook(
474 hooks_utils.trigger_post_push_hook(
475 username=user.username, action='push_local', hook_type='post_push',
475 username=user.username, action='push_local', hook_type='post_push',
476 repo_name=repo_name, repo_alias=repo.alias, commit_ids=[tip.raw_id])
476 repo_name=repo_name, repo_type=repo.alias, commit_ids=[tip.raw_id])
477 return tip
477 return tip
478
478
479 def _sanitize_path(self, f_path):
479 def _sanitize_path(self, f_path):
@@ -799,7 +799,7 b' class ScmModel(BaseModel):'
799 if trigger_push_hook:
799 if trigger_push_hook:
800 hooks_utils.trigger_post_push_hook(
800 hooks_utils.trigger_post_push_hook(
801 username=user.username, action='push_local',
801 username=user.username, action='push_local',
802 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
802 repo_name=repo.repo_name, repo_type=scm_instance.alias,
803 hook_type='post_push',
803 hook_type='post_push',
804 commit_ids=[tip.raw_id])
804 commit_ids=[tip.raw_id])
805 return tip
805 return tip
@@ -864,7 +864,7 b' class ScmModel(BaseModel):'
864 if trigger_push_hook:
864 if trigger_push_hook:
865 hooks_utils.trigger_post_push_hook(
865 hooks_utils.trigger_post_push_hook(
866 username=user.username, action='push_local', hook_type='post_push',
866 username=user.username, action='push_local', hook_type='post_push',
867 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
867 repo_name=repo.repo_name, repo_type=scm_instance.alias,
868 commit_ids=[tip.raw_id])
868 commit_ids=[tip.raw_id])
869
869
870 return tip
870 return tip
@@ -926,7 +926,7 b' class ScmModel(BaseModel):'
926 if trigger_push_hook:
926 if trigger_push_hook:
927 hooks_utils.trigger_post_push_hook(
927 hooks_utils.trigger_post_push_hook(
928 username=user.username, action='push_local', hook_type='post_push',
928 username=user.username, action='push_local', hook_type='post_push',
929 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
929 repo_name=repo.repo_name, repo_type=scm_instance.alias,
930 commit_ids=[tip.raw_id])
930 commit_ids=[tip.raw_id])
931 return tip
931 return tip
932
932
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2019 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -37,17 +37,17 b' from rhodecode.lib.utils2 import ('
37 AttributeDict, str2bool)
37 AttributeDict, str2bool)
38 from rhodecode.lib.exceptions import (
38 from rhodecode.lib.exceptions import (
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError, UserOwnsArtifactsException)
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError,
41 UserOwnsPullRequestsException, UserOwnsArtifactsException)
41 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
43 from rhodecode.model.auth_token import AuthTokenModel
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 _hash_key, true, false, or_, joinedload, User, UserToPerm,
45 _hash_key, true, false, or_, joinedload, User, UserToPerm,
46 UserEmailMap, UserIpMap, UserLog)
46 UserEmailMap, UserIpMap, UserLog)
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
@@ -261,7 +261,7 b' class UserModel(BaseModel):'
261 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
261 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
262
262
263 from rhodecode.lib.auth import (
263 from rhodecode.lib.auth import (
264 get_crypt_password, check_password, generate_auth_token)
264 get_crypt_password, check_password)
265 from rhodecode.lib.hooks_base import (
265 from rhodecode.lib.hooks_base import (
266 log_create_user, check_allowed_create_user)
266 log_create_user, check_allowed_create_user)
267
267
@@ -443,15 +443,16 b' class UserModel(BaseModel):'
443 log.error(traceback.format_exc())
443 log.error(traceback.format_exc())
444 raise
444 raise
445
445
446 def _handle_user_repos(self, username, repositories, handle_mode=None):
446 def _handle_user_repos(self, username, repositories, handle_user,
447 _superadmin = self.cls.get_first_super_admin()
447 handle_mode=None):
448
448 left_overs = True
449 left_overs = True
449
450
450 from rhodecode.model.repo import RepoModel
451 from rhodecode.model.repo import RepoModel
451
452
452 if handle_mode == 'detach':
453 if handle_mode == 'detach':
453 for obj in repositories:
454 for obj in repositories:
454 obj.user = _superadmin
455 obj.user = handle_user
455 # set description we know why we super admin now owns
456 # set description we know why we super admin now owns
456 # additional repositories that were orphaned !
457 # additional repositories that were orphaned !
457 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
458 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
@@ -465,16 +466,16 b' class UserModel(BaseModel):'
465 # if nothing is done we have left overs left
466 # if nothing is done we have left overs left
466 return left_overs
467 return left_overs
467
468
468 def _handle_user_repo_groups(self, username, repository_groups,
469 def _handle_user_repo_groups(self, username, repository_groups, handle_user,
469 handle_mode=None):
470 handle_mode=None):
470 _superadmin = self.cls.get_first_super_admin()
471
471 left_overs = True
472 left_overs = True
472
473
473 from rhodecode.model.repo_group import RepoGroupModel
474 from rhodecode.model.repo_group import RepoGroupModel
474
475
475 if handle_mode == 'detach':
476 if handle_mode == 'detach':
476 for r in repository_groups:
477 for r in repository_groups:
477 r.user = _superadmin
478 r.user = handle_user
478 # set description we know why we super admin now owns
479 # set description we know why we super admin now owns
479 # additional repositories that were orphaned !
480 # additional repositories that were orphaned !
480 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
481 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
@@ -489,8 +490,9 b' class UserModel(BaseModel):'
489 # if nothing is done we have left overs left
490 # if nothing is done we have left overs left
490 return left_overs
491 return left_overs
491
492
492 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
493 def _handle_user_user_groups(self, username, user_groups, handle_user,
493 _superadmin = self.cls.get_first_super_admin()
494 handle_mode=None):
495
494 left_overs = True
496 left_overs = True
495
497
496 from rhodecode.model.user_group import UserGroupModel
498 from rhodecode.model.user_group import UserGroupModel
@@ -499,8 +501,8 b' class UserModel(BaseModel):'
499 for r in user_groups:
501 for r in user_groups:
500 for user_user_group_to_perm in r.user_user_group_to_perm:
502 for user_user_group_to_perm in r.user_user_group_to_perm:
501 if user_user_group_to_perm.user.username == username:
503 if user_user_group_to_perm.user.username == username:
502 user_user_group_to_perm.user = _superadmin
504 user_user_group_to_perm.user = handle_user
503 r.user = _superadmin
505 r.user = handle_user
504 # set description we know why we super admin now owns
506 # set description we know why we super admin now owns
505 # additional repositories that were orphaned !
507 # additional repositories that were orphaned !
506 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
508 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
@@ -514,13 +516,37 b' class UserModel(BaseModel):'
514 # if nothing is done we have left overs left
516 # if nothing is done we have left overs left
515 return left_overs
517 return left_overs
516
518
517 def _handle_user_artifacts(self, username, artifacts, handle_mode=None):
519 def _handle_user_pull_requests(self, username, pull_requests, handle_user,
518 _superadmin = self.cls.get_first_super_admin()
520 handle_mode=None):
521 left_overs = True
522
523 from rhodecode.model.pull_request import PullRequestModel
524
525 if handle_mode == 'detach':
526 for pr in pull_requests:
527 pr.user_id = handle_user.user_id
528 # set description we know why we super admin now owns
529 # additional repositories that were orphaned !
530 pr.description += ' \n::detached pull requests from deleted user: %s' % (username,)
531 self.sa.add(pr)
532 left_overs = False
533 elif handle_mode == 'delete':
534 for pr in pull_requests:
535 PullRequestModel().delete(pr)
536
537 left_overs = False
538
539 # if nothing is done we have left overs left
540 return left_overs
541
542 def _handle_user_artifacts(self, username, artifacts, handle_user,
543 handle_mode=None):
544
519 left_overs = True
545 left_overs = True
520
546
521 if handle_mode == 'detach':
547 if handle_mode == 'detach':
522 for a in artifacts:
548 for a in artifacts:
523 a.upload_user = _superadmin
549 a.upload_user = handle_user
524 # set description we know why we super admin now owns
550 # set description we know why we super admin now owns
525 # additional artifacts that were orphaned !
551 # additional artifacts that were orphaned !
526 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
552 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
@@ -528,7 +554,8 b' class UserModel(BaseModel):'
528 left_overs = False
554 left_overs = False
529 elif handle_mode == 'delete':
555 elif handle_mode == 'delete':
530 from rhodecode.apps.file_store import utils as store_utils
556 from rhodecode.apps.file_store import utils as store_utils
531 storage = store_utils.get_file_storage(self.request.registry.settings)
557 request = get_current_request()
558 storage = store_utils.get_file_storage(request.registry.settings)
532 for a in artifacts:
559 for a in artifacts:
533 file_uid = a.file_uid
560 file_uid = a.file_uid
534 storage.delete(file_uid)
561 storage.delete(file_uid)
@@ -540,11 +567,13 b' class UserModel(BaseModel):'
540 return left_overs
567 return left_overs
541
568
542 def delete(self, user, cur_user=None, handle_repos=None,
569 def delete(self, user, cur_user=None, handle_repos=None,
543 handle_repo_groups=None, handle_user_groups=None, handle_artifacts=None):
570 handle_repo_groups=None, handle_user_groups=None,
571 handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None):
544 from rhodecode.lib.hooks_base import log_delete_user
572 from rhodecode.lib.hooks_base import log_delete_user
545
573
546 if not cur_user:
574 if not cur_user:
547 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
575 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
576
548 user = self._get_user(user)
577 user = self._get_user(user)
549
578
550 try:
579 try:
@@ -552,9 +581,11 b' class UserModel(BaseModel):'
552 raise DefaultUserException(
581 raise DefaultUserException(
553 u"You can't remove this user since it's"
582 u"You can't remove this user since it's"
554 u" crucial for entire application")
583 u" crucial for entire application")
584 handle_user = handle_new_owner or self.cls.get_first_super_admin()
585 log.debug('New detached objects owner %s', handle_user)
555
586
556 left_overs = self._handle_user_repos(
587 left_overs = self._handle_user_repos(
557 user.username, user.repositories, handle_repos)
588 user.username, user.repositories, handle_user, handle_repos)
558 if left_overs and user.repositories:
589 if left_overs and user.repositories:
559 repos = [x.repo_name for x in user.repositories]
590 repos = [x.repo_name for x in user.repositories]
560 raise UserOwnsReposException(
591 raise UserOwnsReposException(
@@ -564,7 +595,7 b' class UserModel(BaseModel):'
564 'list_repos': ', '.join(repos)})
595 'list_repos': ', '.join(repos)})
565
596
566 left_overs = self._handle_user_repo_groups(
597 left_overs = self._handle_user_repo_groups(
567 user.username, user.repository_groups, handle_repo_groups)
598 user.username, user.repository_groups, handle_user, handle_repo_groups)
568 if left_overs and user.repository_groups:
599 if left_overs and user.repository_groups:
569 repo_groups = [x.group_name for x in user.repository_groups]
600 repo_groups = [x.group_name for x in user.repository_groups]
570 raise UserOwnsRepoGroupsException(
601 raise UserOwnsRepoGroupsException(
@@ -574,7 +605,7 b' class UserModel(BaseModel):'
574 'list_repo_groups': ', '.join(repo_groups)})
605 'list_repo_groups': ', '.join(repo_groups)})
575
606
576 left_overs = self._handle_user_user_groups(
607 left_overs = self._handle_user_user_groups(
577 user.username, user.user_groups, handle_user_groups)
608 user.username, user.user_groups, handle_user, handle_user_groups)
578 if left_overs and user.user_groups:
609 if left_overs and user.user_groups:
579 user_groups = [x.users_group_name for x in user.user_groups]
610 user_groups = [x.users_group_name for x in user.user_groups]
580 raise UserOwnsUserGroupsException(
611 raise UserOwnsUserGroupsException(
@@ -582,8 +613,17 b' class UserModel(BaseModel):'
582 u'removed. Switch owners or remove those user groups:%s'
613 u'removed. Switch owners or remove those user groups:%s'
583 % (user.username, len(user_groups), ', '.join(user_groups)))
614 % (user.username, len(user_groups), ', '.join(user_groups)))
584
615
616 left_overs = self._handle_user_pull_requests(
617 user.username, user.user_pull_requests, handle_user, handle_pull_requests)
618 if left_overs and user.user_pull_requests:
619 pull_requests = ['!{}'.format(x.pull_request_id) for x in user.user_pull_requests]
620 raise UserOwnsPullRequestsException(
621 u'user "%s" still owns %s pull requests and cannot be '
622 u'removed. Switch owners or remove those pull requests:%s'
623 % (user.username, len(pull_requests), ', '.join(pull_requests)))
624
585 left_overs = self._handle_user_artifacts(
625 left_overs = self._handle_user_artifacts(
586 user.username, user.artifacts, handle_artifacts)
626 user.username, user.artifacts, handle_user, handle_artifacts)
587 if left_overs and user.artifacts:
627 if left_overs and user.artifacts:
588 artifacts = [x.file_uid for x in user.artifacts]
628 artifacts = [x.file_uid for x in user.artifacts]
589 raise UserOwnsArtifactsException(
629 raise UserOwnsArtifactsException(
@@ -878,7 +918,7 b' class UserModel(BaseModel):'
878 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
918 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
879 parsed_ip_range = []
919 parsed_ip_range = []
880
920
881 for index in xrange(int(start_ip), int(end_ip) + 1):
921 for index in range(int(start_ip), int(end_ip) + 1):
882 new_ip = ipaddress.ip_address(index)
922 new_ip = ipaddress.ip_address(index)
883 parsed_ip_range.append(str(new_ip))
923 parsed_ip_range.append(str(new_ip))
884 ip_list.extend(parsed_ip_range)
924 ip_list.extend(parsed_ip_range)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
3 # Copyright (C) 2017-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,13 +20,40 b''
20
20
21 import logging
21 import logging
22
22
23 import deform
24 import deform.widget
23 import deform.widget
24 from deform.widget import null, OptGroup, string_types
25
26 log = logging.getLogger(__name__)
25
27
26
28
27 log = logging.getLogger(__name__)
29 def _normalize_choices(values):
30 result = []
31 for item in values:
32 if isinstance(item, OptGroup):
33 normalized_options = _normalize_choices(item.options)
34 result.append(OptGroup(item.label, *normalized_options))
35 else:
36 value, description, help_block = item
37 if not isinstance(value, string_types):
38 value = str(value)
39 result.append((value, description, help_block))
40 return result
28
41
29
42
30 class CodeMirrorWidget(deform.widget.TextAreaWidget):
43 class CodeMirrorWidget(deform.widget.TextAreaWidget):
31 template = 'codemirror'
44 template = 'codemirror'
32 requirements = (('deform', None), ('codemirror', None))
45 requirements = (('deform', None), ('codemirror', None))
46
47
48 class CheckboxChoiceWidgetDesc(deform.widget.CheckboxChoiceWidget):
49 template = "checkbox_choice_desc"
50
51 def serialize(self, field, cstruct, **kw):
52 if cstruct in (null, None):
53 cstruct = ()
54 readonly = kw.get("readonly", self.readonly)
55 values = kw.get("values", self.values)
56 kw["values"] = _normalize_choices(values)
57 template = readonly and self.readonly_template or self.template
58 tmpl_values = self.get_template_values(field, cstruct, kw)
59 return field.renderer(template, **tmpl_values)
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -791,7 +791,7 b" def ValidPerms(localizer, type_='repo'):"
791
791
792 # get updates of permissions
792 # get updates of permissions
793 # (read the existing radio button states)
793 # (read the existing radio button states)
794 default_user_id = User.get_default_user().user_id
794 default_user_id = User.get_default_user_id()
795
795
796 for k, update_value in value.iteritems():
796 for k, update_value in value.iteritems():
797 if k.startswith('u_perm_') or k.startswith('g_perm_'):
797 if k.startswith('u_perm_') or k.startswith('g_perm_'):
@@ -265,7 +265,7 b' input[type="button"] {'
265 .btn-action-switcher-container{
265 .btn-action-switcher-container{
266 position: absolute;
266 position: absolute;
267 top: 30px;
267 top: 30px;
268 left: 0px;
268 left: -82px;
269 }
269 }
270
270
271 .btn-action-switcher {
271 .btn-action-switcher {
@@ -378,7 +378,9 b' input[type="button"] {'
378 background-color: transparent;
378 background-color: transparent;
379 border: none;
379 border: none;
380 }
380 }
381 .noselect
381 }
382 }
383
382 .grid_delete {
384 .grid_delete {
383 .action_button {
385 .action_button {
384 border: none;
386 border: none;
@@ -500,7 +500,7 b' div.codeblock {'
500
500
501 img.rendered-binary {
501 img.rendered-binary {
502 height: auto;
502 height: auto;
503 width: 100%;
503 width: auto;
504 }
504 }
505
505
506 .markdown-block {
506 .markdown-block {
@@ -594,9 +594,21 b' div.annotatediv { margin-left: 2px; marg'
594 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
594 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
595
595
596 .code { display: block; border:0px !important; }
596 .code { display: block; border:0px !important; }
597
597 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
598 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
599 .codehilite {
600 /*ElasticMatch is custom RhodeCode TAG*/
601
602 .c-ElasticMatch {
603 background-color: #faffa6;
604 padding: 0.2em;
605 }
606 }
607
598 /* This can be generated with `pygmentize -S default -f html` */
608 /* This can be generated with `pygmentize -S default -f html` */
609 .code-highlight,
599 .codehilite {
610 .codehilite {
611 /*ElasticMatch is custom RhodeCode TAG*/
600 .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;}
612 .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;}
601 .hll { background-color: #ffffcc }
613 .hll { background-color: #ffffcc }
602 .c { color: #408080; font-style: italic } /* Comment */
614 .c { color: #408080; font-style: italic } /* Comment */
@@ -402,3 +402,10 b' input.input-valuedisplay {'
402 }
402 }
403 }
403 }
404 }
404 }
405
406 .repo-type-radio input {
407 margin: 0 0 0 0
408 }
409 .repo-type-radio label {
410 padding-right: 15px;
411 }
@@ -27,6 +27,8 b''
27 @import 'panels';
27 @import 'panels';
28 @import 'deform';
28 @import 'deform';
29 @import 'tooltips';
29 @import 'tooltips';
30 @import 'sweetalert2';
31
30
32
31 //--- BASE ------------------//
33 //--- BASE ------------------//
32 .noscript-error {
34 .noscript-error {
@@ -419,8 +421,8 b' ul.auth_plugins {'
419 }
421 }
420
422
421 .pr-title-input {
423 .pr-title-input {
422 width: 80%;
424 width: 100%;
423 font-size: 1em;
425 font-size: 18px;
424 margin: 0 0 4px 0;
426 margin: 0 0 4px 0;
425 padding: 0;
427 padding: 0;
426 line-height: 1.7em;
428 line-height: 1.7em;
@@ -440,6 +442,7 b' ul.auth_plugins {'
440 color: black;
442 color: black;
441 opacity: 1;
443 opacity: 1;
442 background: #fff;
444 background: #fff;
445 font-size: 18px;
443 }
446 }
444 }
447 }
445
448
@@ -464,9 +467,9 b' ul.auth_plugins {'
464 #pr_open_message {
467 #pr_open_message {
465 border: @border-thickness solid #fff;
468 border: @border-thickness solid #fff;
466 border-radius: @border-radius;
469 border-radius: @border-radius;
467 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
468 text-align: left;
470 text-align: left;
469 overflow: hidden;
471 overflow: hidden;
472 white-space: pre-line;
470 }
473 }
471
474
472 .pr-details-title {
475 .pr-details-title {
@@ -1483,10 +1486,16 b' table.integrations {'
1483 border-bottom: @border-thickness solid @grey5;
1486 border-bottom: @border-thickness solid @grey5;
1484 margin-bottom: @space;
1487 margin-bottom: @space;
1485 }
1488 }
1489
1486 .reviewers-title {
1490 .reviewers-title {
1487 width: 25%;
1491 width: 25%;
1488 min-width: 200px;
1492 min-width: 200px;
1493
1494 &.first-panel {
1495 margin-top: 34px;
1496 }
1489 }
1497 }
1498
1490 .reviewers {
1499 .reviewers {
1491 width: 25%;
1500 width: 25%;
1492 min-width: 200px;
1501 min-width: 200px;
@@ -1787,7 +1796,7 b' table.integrations {'
1787 }
1796 }
1788
1797
1789 div.ancestor {
1798 div.ancestor {
1790 line-height: 33px;
1799
1791 }
1800 }
1792
1801
1793 .cs_icon_td input[type="checkbox"] {
1802 .cs_icon_td input[type="checkbox"] {
@@ -2094,6 +2103,12 b' BIN_FILENODE = 7'
2094 }
2103 }
2095 }
2104 }
2096
2105
2106 .notice-messages {
2107 .markdown-block,
2108 .rst-block {
2109 padding: 0;
2110 }
2111 }
2097
2112
2098 .notifications_buttons{
2113 .notifications_buttons{
2099 float: right;
2114 float: right;
@@ -2519,6 +2534,10 b' h3.files_location{'
2519 white-space: nowrap;
2534 white-space: nowrap;
2520 }
2535 }
2521
2536
2537 .upload-form {
2538 margin-top: 46px;
2539 }
2540
2522 .location-path {
2541 .location-path {
2523 width: -moz-available; /* WebKit-based browsers will ignore this. */
2542 width: -moz-available; /* WebKit-based browsers will ignore this. */
2524 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2543 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
@@ -820,7 +820,53 b' input {'
820 }
820 }
821
821
822 .menulabel-notice {
822 .menulabel-notice {
823 border: 1px solid @color5;
823
824 padding:7px 10px;
824 padding:7px 10px;
825
826 &.notice-warning {
827 border: 1px solid @color3;
828 .notice-color-warning
829 }
830 &.notice-error {
831 border: 1px solid @color5;
832 .notice-color-error
833 }
834 &.notice-info {
835 border: 1px solid @color1;
836 .notice-color-info
837 }
838 }
839
840 .notice-messages-container {
841 position: absolute;
842 top: 45px;
843 }
844
845 .notice-messages {
846 display: block;
847 position: relative;
848 z-index: 300;
849 min-width: 500px;
850 max-width: 500px;
851 min-height: 100px;
852 margin-top: 4px;
853 margin-bottom: 24px;
854 font-size: 14px;
855 font-weight: 400;
856 padding: 8px 0;
857 background-color: #fff;
858 border: 1px solid @grey4;
859 box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.07);
860 }
861
862 .notice-color-warning {
863 color: @color3;
864 }
865
866 .notice-color-error {
825 color: @color5;
867 color: @color5;
826 }
868 }
869
870 .notice-color-info {
871 color: @color1;
872 }
@@ -269,6 +269,7 b''
269 .icon-expand-linked { cursor: pointer; color: @grey3; font-size: 14px }
269 .icon-expand-linked { cursor: pointer; color: @grey3; font-size: 14px }
270 .icon-more-linked { cursor: pointer; color: @grey3 }
270 .icon-more-linked { cursor: pointer; color: @grey3 }
271 .icon-flag-filled-red { color: @color5 !important; }
271 .icon-flag-filled-red { color: @color5 !important; }
272 .icon-filled-red { color: @color5 !important; }
272
273
273 .repo-switcher-dropdown .select2-result-label {
274 .repo-switcher-dropdown .select2-result-label {
274 .icon-git:before {
275 .icon-git:before {
@@ -205,7 +205,9 b' select.select2{height:28px;visibility:hi'
205 font-family: @text-regular;
205 font-family: @text-regular;
206 color: @grey2;
206 color: @grey2;
207 cursor: pointer;
207 cursor: pointer;
208 white-space: nowrap;
208 }
209 }
210
209 &.select2-result-with-children {
211 &.select2-result-with-children {
210
212
211 .select2-result-label {
213 .select2-result-label {
@@ -220,6 +222,7 b' select.select2{height:28px;visibility:hi'
220 font-family: @text-regular;
222 font-family: @text-regular;
221 color: @grey2;
223 color: @grey2;
222 cursor: pointer;
224 cursor: pointer;
225 white-space: nowrap;
223 }
226 }
224 }
227 }
225 }
228 }
@@ -352,7 +352,7 b' table.dataTable {'
352 margin-bottom: @padding;
352 margin-bottom: @padding;
353
353
354 table.rctable td:first-child {
354 table.rctable td:first-child {
355 width: 340px;
355 width: 120px;
356 }
356 }
357 }
357 }
358
358
@@ -110,6 +110,8 b' function registerRCRoutes() {'
110 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
110 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
111 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
111 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
112 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
112 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
113 pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']);
114 pyroutes.register('edit_user_auth_tokens_view', '/_admin/users/%(user_id)s/edit/auth_tokens/view', ['user_id']);
113 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
115 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
114 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
116 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
115 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
117 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
@@ -203,6 +205,7 b' function registerRCRoutes() {'
203 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
204 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
211 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
@@ -310,6 +313,7 b' function registerRCRoutes() {'
310 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
313 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
311 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
314 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
312 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
315 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
316 pyroutes.register('my_account_auth_tokens_view', '/_admin/my_account/auth_tokens/view', []);
313 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
317 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
314 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
318 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
315 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
319 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2016-2019 RhodeCode GmbH
1 // # Copyright (C) 2016-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 b' var _gettext = function (s) {'
22 if (_TM.hasOwnProperty(s)) {
22 if (_TM.hasOwnProperty(s)) {
23 return _TM[s];
23 return _TM[s];
24 }
24 }
25 i18nLog.error(
25 i18nLog.warning(
26 'String `' + s + '` was requested but cannot be ' +
26 'String `' + s + '` was requested but cannot be ' +
27 'found in translation table');
27 'found in translation table');
28 return s
28 return s
@@ -34,3 +34,5 b' var _ngettext = function (singular, plur'
34 }
34 }
35 return _gettext(plural)
35 return _gettext(plural)
36 };
36 };
37
38
@@ -113,6 +113,10 b''
113 this.invoke(Logger.WARN, arguments);
113 this.invoke(Logger.WARN, arguments);
114 },
114 },
115
115
116 warning: function () {
117 this.invoke(Logger.WARN, arguments);
118 },
119
116 error: function () {
120 error: function () {
117 this.invoke(Logger.ERROR, arguments);
121 this.invoke(Logger.ERROR, arguments);
118 },
122 },
@@ -151,6 +155,7 b''
151 L.timeEnd = bind(globalLogger, globalLogger.timeEnd);
155 L.timeEnd = bind(globalLogger, globalLogger.timeEnd);
152 L.info = bind(globalLogger, globalLogger.info);
156 L.info = bind(globalLogger, globalLogger.info);
153 L.warn = bind(globalLogger, globalLogger.warn);
157 L.warn = bind(globalLogger, globalLogger.warn);
158 L.warning = bind(globalLogger, globalLogger.warning);
154 L.error = bind(globalLogger, globalLogger.error);
159 L.error = bind(globalLogger, globalLogger.error);
155
160
156 // Don't forget the convenience alias!
161 // Don't forget the convenience alias!
@@ -564,13 +564,15 b''
564
564
565 $.extend(ajaxSettings, options.ajaxSettings);
565 $.extend(ajaxSettings, options.ajaxSettings);
566
566
567 that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
567 that.currentRequest = $.ajax(ajaxSettings)
568 .done(function (data) {
568 var result;
569 var result;
569 that.currentRequest = null;
570 that.currentRequest = null;
570 result = options.transformResult(data);
571 result = options.transformResult(data);
571 that.processResponse(result, query, cacheKey);
572 that.processResponse(result, query, cacheKey);
572 options.onSearchComplete.call(that.element, query, result.suggestions);
573 options.onSearchComplete.call(that.element, query, result.suggestions);
573 }).fail(function (jqXHR, textStatus, errorThrown) {
574 })
575 .fail(function (jqXHR, textStatus, errorThrown) {
574 options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown);
576 options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown);
575 });
577 });
576 }
578 }
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2016-2019 RhodeCode GmbH
1 // # Copyright (C) 2016-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -347,11 +347,10 b' var _submitAjaxPOST = function(url, post'
347 self.globalSubmitSuccessCallback();
347 self.globalSubmitSuccessCallback();
348
348
349 };
349 };
350 var submitFailCallback = function(data) {
350 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
351 alert(
351 var prefix = "Error while submitting comment.\n"
352 "Error while submitting comment.\n" +
352 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
353 "Error code {0} ({1}).".format(data.status, data.statusText)
353 ajaxErrorSwal(message);
354 );
355 self.resetCommentFormState(text);
354 self.resetCommentFormState(text);
356 };
355 };
357 self.submitAjaxPOST(
356 self.submitAjaxPOST(
@@ -447,11 +446,11 b' var _submitAjaxPOST = function(url, post'
447 $(self.previewContainer).show();
446 $(self.previewContainer).show();
448
447
449 // by default we reset state of comment preserving the text
448 // by default we reset state of comment preserving the text
450 var previewFailCallback = function(data){
449 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
451 alert(
450 var prefix = "Error while preview of comment.\n"
452 "Error while preview of comment.\n" +
451 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
453 "Error code {0} ({1}).".format(data.status, data.statusText)
452 ajaxErrorSwal(message);
454 );
453
455 self.resetCommentFormState(text)
454 self.resetCommentFormState(text)
456 };
455 };
457 self.submitAjaxPOST(
456 self.submitAjaxPOST(
@@ -556,10 +555,7 b' var CommentsController = function() {'
556 return self.scrollToComment(node, -1, true);
555 return self.scrollToComment(node, -1, true);
557 };
556 };
558
557
559 this.deleteComment = function(node) {
558 this._deleteComment = function(node) {
560 if (!confirm(_gettext('Delete this comment?'))) {
561 return false;
562 }
563 var $node = $(node);
559 var $node = $(node);
564 var $td = $node.closest('td');
560 var $td = $node.closest('td');
565 var $comment = $node.closest('.comment');
561 var $comment = $node.closest('.comment');
@@ -576,13 +572,33 b' var CommentsController = function() {'
576 $comment.remove();
572 $comment.remove();
577 return false;
573 return false;
578 };
574 };
579 var failure = function(data, textStatus, xhr) {
575 var failure = function(jqXHR, textStatus, errorThrown) {
580 alert("error processing request: " + textStatus);
576 var prefix = "Error while deleting this comment.\n"
577 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
578 ajaxErrorSwal(message);
579
581 $comment.show('fast');
580 $comment.show('fast');
582 $comment.removeClass('comment-deleting');
581 $comment.removeClass('comment-deleting');
583 return false;
582 return false;
584 };
583 };
585 ajaxPOST(url, postData, success, failure);
584 ajaxPOST(url, postData, success, failure);
585 }
586
587 this.deleteComment = function(node) {
588 var $comment = $(node).closest('.comment');
589 var comment_id = $comment.attr('data-comment-id');
590
591 SwalNoAnimation.fire({
592 title: 'Delete this comment?',
593 icon: 'warning',
594 showCancelButton: true,
595 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
596
597 }).then(function(result) {
598 if (result.value) {
599 self._deleteComment(node);
600 }
601 })
586 };
602 };
587
603
588 this.toggleWideMode = function (node) {
604 this.toggleWideMode = function (node) {
@@ -879,11 +895,10 b' var CommentsController = function() {'
879 commentForm.setActionButtonsDisabled(false);
895 commentForm.setActionButtonsDisabled(false);
880
896
881 };
897 };
882 var submitFailCallback = function(data){
898 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
883 alert(
899 var prefix = "Error while submitting comment.\n"
884 "Error while submitting comment.\n" +
900 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
885 "Error code {0} ({1}).".format(data.status, data.statusText)
901 ajaxErrorSwal(message);
886 );
887 commentForm.resetCommentFormState(text)
902 commentForm.resetCommentFormState(text)
888 };
903 };
889 commentForm.submitAjaxPOST(
904 commentForm.submitAjaxPOST(
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -372,9 +372,13 b' var getFilesMetadata = function() {'
372 var url_data = {
372 var url_data = {
373 'repo_name': templateContext.repo_name,
373 'repo_name': templateContext.repo_name,
374 'commit_id': state.commit_id,
374 'commit_id': state.commit_id,
375 'f_path': state.f_path
375 'f_path': state.f_path,
376 };
376 };
377
377
378 if (atRef !== '') {
379 url_data['at'] = atRef
380 }
381
378 var url = pyroutes.url('repo_nodetree_full', url_data);
382 var url = pyroutes.url('repo_nodetree_full', url_data);
379
383
380 metadataRequest = $.ajax({url: url});
384 metadataRequest = $.ajax({url: url});
@@ -517,3 +521,80 b' var showAuthors = function(elem, annotat'
517 return FileEditor;
521 return FileEditor;
518 });
522 });
519
523
524
525 var checkFileHead = function($editForm, commit_id, f_path, operation) {
526 function getFormData($form){
527 var unindexed_array = $form.serializeArray();
528 var indexed_array = {};
529
530 $.map(unindexed_array, function(n, i){
531 indexed_array[n['name']] = n['value'];
532 });
533
534 return indexed_array;
535 }
536
537 $editForm.on('submit', function (e) {
538
539 var validHead = $editForm.find('#commit_btn').data('validHead');
540 if (validHead === true){
541 return true
542 }
543
544 // no marker, we do "checks"
545 e.preventDefault();
546 var formData = getFormData($editForm);
547 var new_path = formData['filename'];
548
549 var success = function(data) {
550
551 if (data['is_head'] === true && data['path_exists'] === "") {
552
553 $editForm.find('#commit_btn').data('validHead', true);
554 $editForm.find('#commit_btn').val('Committing...');
555 $editForm.submit();
556
557 } else {
558 var message = '';
559 var urlTmpl = '<a target="_blank" href="{0}">here</a>';
560 $editForm.find('#commit_btn').val('Commit aborted');
561
562 if (operation === 'edit') {
563 var new_url = urlTmpl.format(pyroutes.url('repo_files_edit_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path}));
564 message = _gettext('File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version.'.format(f_path, new_url));
565 } else if (operation === 'delete') {
566 var new_url = urlTmpl.format(pyroutes.url('repo_files_remove_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path}));
567 message = _gettext('File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version.'.format(f_path, new_url));
568 } else if (operation === 'create') {
569 if (data['path_exists'] !== "") {
570 message = _gettext('There is an existing path `{0}` at this commit.'.format(data['path_exists']));
571 } else {
572 var new_url = urlTmpl.format(pyroutes.url('repo_files_add_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path, "filename": new_path}));
573 message = _gettext('There is a later version of file tree available. Click {0} to create a file at the latest tree.'.format(new_url));
574 }
575 }
576
577 var payload = {
578 message: {
579 message: message,
580 level: 'warning',
581 force: true
582 }
583 };
584 $.Topic('/notifications').publish(payload);
585 }
586 };
587
588 // lock button
589 $editForm.find('#commit_btn').attr('disabled', 'disabled');
590 $editForm.find('#commit_btn').val('Checking commit...');
591
592 var postData = {'csrf_token': CSRF_TOKEN, 'path': new_path, 'operation': operation};
593 ajaxPOST(pyroutes.url('repo_files_check_head',
594 {'repo_name': templateContext.repo_name, 'commit_id': commit_id, 'f_path': f_path}),
595 postData, success);
596
597 return false
598
599 });
600 };
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -75,12 +75,12 b' var getTitleAndDescription = function(so'
75 var desc = '';
75 var desc = '';
76
76
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
78 var rawMessage = $(value).find('td.td-description .message').data('messageRaw').toString();
78 var rawMessage = value['message'];
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
80 });
80 });
81 // only 1 commit, use commit message as title
81 // only 1 commit, use commit message as title
82 if (elements.length === 1) {
82 if (elements.length === 1) {
83 var rawMessage = $(elements[0]).find('td.td-description .message').data('messageRaw').toString();
83 var rawMessage = elements[0]['message'];
84 title = rawMessage.split('\n')[0];
84 title = rawMessage.split('\n')[0];
85 }
85 }
86 else {
86 else {
@@ -92,7 +92,6 b' var getTitleAndDescription = function(so'
92 };
92 };
93
93
94
94
95
96 ReviewersController = function () {
95 ReviewersController = function () {
97 var self = this;
96 var self = this;
98 this.$reviewRulesContainer = $('#review_rules');
97 this.$reviewRulesContainer = $('#review_rules');
@@ -100,28 +99,35 b' ReviewersController = function () {'
100 this.forbidReviewUsers = undefined;
99 this.forbidReviewUsers = undefined;
101 this.$reviewMembers = $('#review_members');
100 this.$reviewMembers = $('#review_members');
102 this.currentRequest = null;
101 this.currentRequest = null;
102 this.diffData = null;
103 //dummy handler, we might register our own later
104 this.diffDataHandler = function(data){};
103
105
104 this.defaultForbidReviewUsers = function() {
106 this.defaultForbidReviewUsers = function () {
105 return [
107 return [
106 {'username': 'default',
108 {
107 'user_id': templateContext.default_user.user_id}
109 'username': 'default',
110 'user_id': templateContext.default_user.user_id
111 }
108 ];
112 ];
109 };
113 };
110
114
111 this.hideReviewRules = function() {
115 this.hideReviewRules = function () {
112 self.$reviewRulesContainer.hide();
116 self.$reviewRulesContainer.hide();
113 };
117 };
114
118
115 this.showReviewRules = function() {
119 this.showReviewRules = function () {
116 self.$reviewRulesContainer.show();
120 self.$reviewRulesContainer.show();
117 };
121 };
118
122
119 this.addRule = function(ruleText) {
123 this.addRule = function (ruleText) {
120 self.showReviewRules();
124 self.showReviewRules();
121 return '<div>- {0}</div>'.format(ruleText)
125 return '<div>- {0}</div>'.format(ruleText)
122 };
126 };
123
127
124 this.loadReviewRules = function(data) {
128 this.loadReviewRules = function (data) {
129 self.diffData = data;
130
125 // reset forbidden Users
131 // reset forbidden Users
126 this.forbidReviewUsers = self.defaultForbidReviewUsers();
132 this.forbidReviewUsers = self.defaultForbidReviewUsers();
127
133
@@ -141,7 +147,7 b' ReviewersController = function () {'
141 if (data.rules.voting < 0) {
147 if (data.rules.voting < 0) {
142 self.$rulesList.append(
148 self.$rulesList.append(
143 self.addRule(
149 self.addRule(
144 _gettext('All individual reviewers must vote.'))
150 _gettext('All individual reviewers must vote.'))
145 )
151 )
146 } else if (data.rules.voting === 1) {
152 } else if (data.rules.voting === 1) {
147 self.$rulesList.append(
153 self.$rulesList.append(
@@ -158,7 +164,7 b' ReviewersController = function () {'
158 }
164 }
159
165
160 if (data.rules.voting_groups !== undefined) {
166 if (data.rules.voting_groups !== undefined) {
161 $.each(data.rules.voting_groups, function(index, rule_data) {
167 $.each(data.rules.voting_groups, function (index, rule_data) {
162 self.$rulesList.append(
168 self.$rulesList.append(
163 self.addRule(rule_data.text)
169 self.addRule(rule_data.text)
164 )
170 )
@@ -188,7 +194,7 b' ReviewersController = function () {'
188 if (data.rules.forbid_commit_author_to_review) {
194 if (data.rules.forbid_commit_author_to_review) {
189
195
190 if (data.rules_data.forbidden_users) {
196 if (data.rules_data.forbidden_users) {
191 $.each(data.rules_data.forbidden_users, function(index, member_data) {
197 $.each(data.rules_data.forbidden_users, function (index, member_data) {
192 self.forbidReviewUsers.push(member_data)
198 self.forbidReviewUsers.push(member_data)
193 });
199 });
194
200
@@ -203,11 +209,10 b' ReviewersController = function () {'
203 return self.forbidReviewUsers
209 return self.forbidReviewUsers
204 };
210 };
205
211
206 this.loadDefaultReviewers = function(sourceRepo, sourceRef, targetRepo, targetRef) {
212 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
207
213
208 if (self.currentRequest) {
214 if (self.currentRequest) {
209 // make sure we cleanup old running requests before triggering this
215 // make sure we cleanup old running requests before triggering this again
210 // again
211 self.currentRequest.abort();
216 self.currentRequest.abort();
212 }
217 }
213
218
@@ -218,6 +223,9 b' ReviewersController = function () {'
218 prButtonLock(true, null, 'reviewers');
223 prButtonLock(true, null, 'reviewers');
219 $('#user').hide(); // hide user autocomplete before load
224 $('#user').hide(); // hide user autocomplete before load
220
225
226 // lock PR button, so we cannot send PR before it's calculated
227 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
228
221 if (sourceRef.length !== 3 || targetRef.length !== 3) {
229 if (sourceRef.length !== 3 || targetRef.length !== 3) {
222 // don't load defaults in case we're missing some refs...
230 // don't load defaults in case we're missing some refs...
223 $('.calculate-reviewers').hide();
231 $('.calculate-reviewers').hide();
@@ -225,58 +233,80 b' ReviewersController = function () {'
225 }
233 }
226
234
227 var url = pyroutes.url('repo_default_reviewers_data',
235 var url = pyroutes.url('repo_default_reviewers_data',
228 {
236 {
229 'repo_name': templateContext.repo_name,
237 'repo_name': templateContext.repo_name,
230 'source_repo': sourceRepo,
238 'source_repo': sourceRepo,
231 'source_ref': sourceRef[2],
239 'source_ref': sourceRef[2],
232 'target_repo': targetRepo,
240 'target_repo': targetRepo,
233 'target_ref': targetRef[2]
241 'target_ref': targetRef[2]
234 });
242 });
235
243
236 self.currentRequest = $.get(url)
244 self.currentRequest = $.ajax({
237 .done(function(data) {
245 url: url,
246 headers: {'X-PARTIAL-XHR': true},
247 type: 'GET',
248 success: function (data) {
249
238 self.currentRequest = null;
250 self.currentRequest = null;
239
251
240 // review rules
252 // review rules
241 self.loadReviewRules(data);
253 self.loadReviewRules(data);
254 self.handleDiffData(data["diff_info"]);
242
255
243 for (var i = 0; i < data.reviewers.length; i++) {
256 for (var i = 0; i < data.reviewers.length; i++) {
244 var reviewer = data.reviewers[i];
257 var reviewer = data.reviewers[i];
245 self.addReviewMember(
258 self.addReviewMember(reviewer, reviewer.reasons, reviewer.mandatory);
246 reviewer, reviewer.reasons, reviewer.mandatory);
247 }
259 }
248 $('.calculate-reviewers').hide();
260 $('.calculate-reviewers').hide();
249 prButtonLock(false, null, 'reviewers');
261 prButtonLock(false, null, 'reviewers');
250 $('#user').show(); // show user autocomplete after load
262 $('#user').show(); // show user autocomplete after load
251 });
263
264 var commitElements = data["diff_info"]['commits'];
265 if (commitElements.length === 0) {
266 prButtonLock(true, _gettext('no commits'), 'all');
267
268 } else {
269 // un-lock PR button, so we cannot send PR before it's calculated
270 prButtonLock(false, null, 'compare');
271 }
272
273 },
274 error: function (jqXHR, textStatus, errorThrown) {
275 var prefix = "Loading diff and reviewers failed\n"
276 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
277 ajaxErrorSwal(message);
278 }
279 });
280
252 };
281 };
253
282
254 // check those, refactor
283 // check those, refactor
255 this.removeReviewMember = function(reviewer_id, mark_delete) {
284 this.removeReviewMember = function (reviewer_id, mark_delete) {
256 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
285 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
257
286
258 if(typeof(mark_delete) === undefined){
287 if (typeof (mark_delete) === undefined) {
259 mark_delete = false;
288 mark_delete = false;
260 }
289 }
261
290
262 if(mark_delete === true){
291 if (mark_delete === true) {
263 if (reviewer){
292 if (reviewer) {
264 // now delete the input
293 // now delete the input
265 $('#reviewer_{0} input'.format(reviewer_id)).remove();
294 $('#reviewer_{0} input'.format(reviewer_id)).remove();
266 // mark as to-delete
295 // mark as to-delete
267 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
296 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
268 obj.addClass('to-delete');
297 obj.addClass('to-delete');
269 obj.css({"text-decoration":"line-through", "opacity": 0.5});
298 obj.css({"text-decoration": "line-through", "opacity": 0.5});
270 }
299 }
271 }
300 } else {
272 else{
273 $('#reviewer_{0}'.format(reviewer_id)).remove();
301 $('#reviewer_{0}'.format(reviewer_id)).remove();
274 }
302 }
275 };
303 };
276 this.reviewMemberEntry = function() {
304
305 this.reviewMemberEntry = function () {
277
306
278 };
307 };
279 this.addReviewMember = function(reviewer_obj, reasons, mandatory) {
308
309 this.addReviewMember = function (reviewer_obj, reasons, mandatory) {
280 var members = self.$reviewMembers.get(0);
310 var members = self.$reviewMembers.get(0);
281 var id = reviewer_obj.user_id;
311 var id = reviewer_obj.user_id;
282 var username = reviewer_obj.username;
312 var username = reviewer_obj.username;
@@ -287,13 +317,13 b' ReviewersController = function () {'
287 // register IDS to check if we don't have this ID already in
317 // register IDS to check if we don't have this ID already in
288 var currentIds = [];
318 var currentIds = [];
289 var _els = self.$reviewMembers.find('li').toArray();
319 var _els = self.$reviewMembers.find('li').toArray();
290 for (el in _els){
320 for (el in _els) {
291 currentIds.push(_els[el].id)
321 currentIds.push(_els[el].id)
292 }
322 }
293
323
294 var userAllowedReview = function(userId) {
324 var userAllowedReview = function (userId) {
295 var allowed = true;
325 var allowed = true;
296 $.each(self.forbidReviewUsers, function(index, member_data) {
326 $.each(self.forbidReviewUsers, function (index, member_data) {
297 if (parseInt(userId) === member_data['user_id']) {
327 if (parseInt(userId) === member_data['user_id']) {
298 allowed = false;
328 allowed = false;
299 return false // breaks the loop
329 return false // breaks the loop
@@ -303,35 +333,38 b' ReviewersController = function () {'
303 };
333 };
304
334
305 var userAllowed = userAllowedReview(id);
335 var userAllowed = userAllowedReview(id);
306 if (!userAllowed){
336 if (!userAllowed) {
307 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
337 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
308 } else {
338 } else {
309 // only add if it's not there
339 // only add if it's not there
310 var alreadyReviewer = currentIds.indexOf('reviewer_'+id) != -1;
340 var alreadyReviewer = currentIds.indexOf('reviewer_' + id) != -1;
311
341
312 if (alreadyReviewer) {
342 if (alreadyReviewer) {
313 alert(_gettext('User `{0}` already in reviewers').format(username));
343 alert(_gettext('User `{0}` already in reviewers').format(username));
314 } else {
344 } else {
315 members.innerHTML += renderTemplate('reviewMemberEntry', {
345 members.innerHTML += renderTemplate('reviewMemberEntry', {
316 'member': reviewer_obj,
346 'member': reviewer_obj,
317 'mandatory': mandatory,
347 'mandatory': mandatory,
318 'allowed_to_update': true,
348 'allowed_to_update': true,
319 'review_status': 'not_reviewed',
349 'review_status': 'not_reviewed',
320 'review_status_label': _gettext('Not Reviewed'),
350 'review_status_label': _gettext('Not Reviewed'),
321 'reasons': reasons,
351 'reasons': reasons,
322 'create': true
352 'create': true
323 });
353 });
324 tooltipActivate();
354 tooltipActivate();
325 }
355 }
326 }
356 }
327
357
328 };
358 };
329
359
330 this.updateReviewers = function(repo_name, pull_request_id){
360 this.updateReviewers = function (repo_name, pull_request_id) {
331 var postData = $('#reviewers input').serialize();
361 var postData = $('#reviewers input').serialize();
332 _updatePullRequest(repo_name, pull_request_id, postData);
362 _updatePullRequest(repo_name, pull_request_id, postData);
333 };
363 };
334
364
365 this.handleDiffData = function (data) {
366 self.diffDataHandler(data)
367 }
335 };
368 };
336
369
337
370
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -47,3 +47,51 b' var UsersAutoComplete = function(input_i'
47 lookupFilter: autocompleteFilterResult
47 lookupFilter: autocompleteFilterResult
48 });
48 });
49 };
49 };
50
51 var _showAuthToken = function (authTokenId, showUrl) {
52
53 SwalNoAnimation.fire({
54 title: _gettext('Show this authentication token?'),
55 showCancelButton: true,
56 confirmButtonText: _gettext('Show'),
57 showLoaderOnConfirm: true,
58 allowOutsideClick: function () {
59 !Swal.isLoading()
60 },
61 preConfirm: function () {
62
63 var postData = {
64 'auth_token_id': authTokenId,
65 'csrf_token': CSRF_TOKEN
66 };
67 return new Promise(function (resolve, reject) {
68 $.ajax({
69 type: 'POST',
70 data: postData,
71 url: showUrl,
72 headers: {'X-PARTIAL-XHR': true}
73 })
74 .done(function (data) {
75 resolve(data);
76 })
77 .fail(function (jqXHR, textStatus, errorThrown) {
78 //reject("Failed to fetch Authentication Token")
79 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
80 ajaxErrorSwal(message);
81 });
82 })
83 }
84
85 })
86 .then(function (result) {
87 if (result.value) {
88 var tmpl = ('<code>{0}</code>' +
89 '<i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="{1}" title="Copy Token"></i>');
90
91 SwalNoAnimation.fire({
92 title: _gettext('Authentication Token'),
93 html: tmpl.format(result.value.auth_token, result.value.auth_token),
94 })
95 }
96 })
97 } No newline at end of file
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -36,37 +36,140 b' var toQueryString = function(o) {'
36 /**
36 /**
37 * ajax call wrappers
37 * ajax call wrappers
38 */
38 */
39 var ajaxGET = function(url, success, failure) {
39
40 var sUrl = url;
40 var ajaxGET = function (url, success, failure) {
41 var request = $.ajax({url: sUrl, headers: {'X-PARTIAL-XHR': true}})
41 var sUrl = url;
42 .done(function(data){
42 var request = $.ajax({
43 success(data);
43 url: sUrl,
44 })
44 headers: {'X-PARTIAL-XHR': true}
45 .fail(function(data, textStatus, xhr) {
45 })
46 if (failure) {
46 .done(function (data) {
47 failure(data, textStatus, xhr);
47 success(data);
48 } else {
48 })
49 alert("Error processing request. \n" +
49 .fail(function (jqXHR, textStatus, errorThrown) {
50 "Error code {0} ({1}).".format(data.status, data.statusText));
50 if (failure) {
51 }
51 failure(jqXHR, textStatus, errorThrown);
52 });
52 } else {
53 return request;
53 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
54 ajaxErrorSwal(message);
55 }
56 });
57 return request;
58 };
59
60 var ajaxPOST = function (url, postData, success, failure) {
61 var sUrl = url;
62 var postData = toQueryString(postData);
63 var request = $.ajax({
64 type: 'POST',
65 url: sUrl,
66 data: postData,
67 headers: {'X-PARTIAL-XHR': true}
68 })
69 .done(function (data) {
70 success(data);
71 })
72 .fail(function (jqXHR, textStatus, errorThrown) {
73 if (failure) {
74 failure(jqXHR, textStatus, errorThrown);
75 } else {
76 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
77 ajaxErrorSwal(message);
78 }
79 });
80 return request;
54 };
81 };
55 var ajaxPOST = function(url, postData, success, failure) {
82
56 var sUrl = url;
83
57 var postData = toQueryString(postData);
84 SwalNoAnimation = Swal.mixin({
58 var request = $.ajax({type: 'POST', data: postData, url: sUrl,
85 confirmButtonColor: '#84a5d2',
59 headers: {'X-PARTIAL-XHR': true}})
86 cancelButtonColor: '#e85e4d',
60 .done(function(data){
87 showClass: {
61 success(data);
88 popup: 'swal2-noanimation',
62 })
89 backdrop: 'swal2-noanimation'
63 .fail(function(data, textStatus, xhr){
90 },
64 if (failure) {
91 hideClass: {
65 failure(data, textStatus, xhr);
92 popup: '',
93 backdrop: ''
94 },
95 })
96
97
98 /* Example usage:
99 *
100 error: function(jqXHR, textStatus, errorThrown) {
101 var prefix = "Error while fetching entries.\n"
102 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
103 ajaxErrorSwal(message);
104 }
105 *
106 * */
107 function formatErrorMessage(jqXHR, textStatus, errorThrown, prefix) {
108 if(typeof prefix === "undefined") {
109 prefix = ''
110 }
111
112 if (jqXHR.status === 0) {
113 return (prefix + 'Not connected.\nPlease verify your network connection.');
114 } else if (jqXHR.status == 401) {
115 return (prefix + 'Unauthorized access. [401]');
116 } else if (jqXHR.status == 404) {
117 return (prefix + 'The requested page not found. [404]');
118 } else if (jqXHR.status == 500) {
119 return (prefix + 'Internal Server Error [500].');
120 } else if (jqXHR.status == 503) {
121 return (prefix + 'Service unavailable [503].');
122 } else if (errorThrown === 'parsererror') {
123 return (prefix + 'Requested JSON parse failed.');
124 } else if (errorThrown === 'timeout') {
125 return (prefix + 'Time out error.');
126 } else if (errorThrown === 'abort') {
127 return (prefix + 'Ajax request aborted.');
66 } else {
128 } else {
67 alert("Error processing request. \n" +
129 return (prefix + 'Uncaught Error.\n' + jqXHR.responseText);
68 "Error code {0} ({1}).".format(data.status, data.statusText));
69 }
130 }
70 });
131 }
71 return request;
132
72 };
133 function ajaxErrorSwal(message) {
134 SwalNoAnimation.fire({
135 icon: 'error',
136 title: _gettext('Ajax Request Error'),
137 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
138 showClass: {
139 popup: 'swal2-noanimation',
140 backdrop: 'swal2-noanimation'
141 },
142 hideClass: {
143 popup: '',
144 backdrop: ''
145 }
146 })
147 }
148
149 /*
150 * use in onclick attributes e.g
151 * onclick="submitConfirm(event, this, _gettext('Confirm to delete '), _gettext('Confirm Delete'), 'what we delete')">
152 * */
153 function submitConfirm(event, self, question, confirmText, htmlText) {
154 if (htmlText === "undefined") {
155 htmlText = null;
156 }
157 if (confirmText === "undefined") {
158 confirmText = _gettext('Delete')
159 }
160 event.preventDefault();
161
162 SwalNoAnimation.fire({
163 title: question,
164 icon: 'warning',
165 html: htmlText,
166
167 showCancelButton: true,
168
169 confirmButtonText: confirmText
170 }).then(function(result) {
171 if (result.value) {
172 $(self).closest("form").submit();
173 }
174 })
175 } No newline at end of file
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
@@ -393,6 +393,7 b' html[dir="rtl"] .select2-results {'
393 -moz-user-select: none;
393 -moz-user-select: none;
394 -ms-user-select: none;
394 -ms-user-select: none;
395 user-select: none;
395 user-select: none;
396 white-space: nowrap;
396 }
397 }
397
398
398 .select2-results-dept-1 .select2-result-label { padding-left: 20px }
399 .select2-results-dept-1 .select2-result-label { padding-left: 20px }
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -54,7 +54,10 b''
54 %if c.is_super_admin or c.gist.gist_owner == c.rhodecode_user.user_id:
54 %if c.is_super_admin or c.gist.gist_owner == c.rhodecode_user.user_id:
55 <div class="pull-right remove_gist">
55 <div class="pull-right remove_gist">
56 ${h.secure_form(h.route_path('gist_delete', gist_id=c.gist.gist_access_id), request=request)}
56 ${h.secure_form(h.route_path('gist_delete', gist_id=c.gist.gist_access_id), request=request)}
57 ${h.submit('remove_gist', _('Delete'),class_="btn btn-mini btn-danger",onclick="return confirm('"+_('Confirm to delete this Gist')+"');")}
57 <input class="btn btn-mini btn-danger" id="remove_gist" name="remove_gist"
58 onclick="submitConfirm(event, this, _gettext('Confirm to delete this gist'), _gettext('Delete'), '${c.gist.gist_access_id}')"
59 type="submit" value="${_('Delete')}"
60 >
58 ${h.end_form()}
61 ${h.end_form()}
59 </div>
62 </div>
60 %endif
63 %endif
@@ -1,4 +1,10 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <script>
3 var showAuthToken = function(authTokenId) {
4 return _showAuthToken(authTokenId, pyroutes.url('my_account_auth_tokens_view'))
5 }
6 </script>
7
2 <div class="panel-heading">
8 <div class="panel-heading">
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
9 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 </div>
10 </div>
@@ -21,9 +27,11 b''
21 %if c.user_auth_tokens:
27 %if c.user_auth_tokens:
22 %for auth_token in c.user_auth_tokens:
28 %for auth_token in c.user_auth_tokens:
23 <tr class="${('expired' if auth_token.expired else '')}">
29 <tr class="${('expired' if auth_token.expired else '')}">
24 <td class="truncate-wrap td-authtoken">
30 <td class="td-authtoken">
25 <div class="user_auth_tokens truncate autoexpand">
31 <div class="user_auth_tokens">
26 <code>${auth_token.api_key}</code>
32 <code class="cursor-pointer" onclick="showAuthToken(${auth_token.user_api_key_id})">
33 ${auth_token.token_obfuscated}
34 </code>
27 </div>
35 </div>
28 </td>
36 </td>
29 <td class="td-wrap">${auth_token.description}</td>
37 <td class="td-wrap">${auth_token.description}</td>
@@ -44,9 +52,10 b''
44 </td>
52 </td>
45 <td class="td-action">
53 <td class="td-action">
46 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)}
54 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)}
47 ${h.hidden('del_auth_token', auth_token.user_api_key_id)}
55 <input name="del_auth_token" type="hidden" value="${auth_token.user_api_key_id}">
48 <button class="btn btn-link btn-danger" type="submit"
56 <button class="btn btn-link btn-danger" type="submit"
49 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');">
57 onclick="submitConfirm(event, this, _gettext('Confirm to delete this auth token'), _gettext('Delete'), '${auth_token.token_obfuscated}')"
58 >
50 ${_('Delete')}
59 ${_('Delete')}
51 </button>
60 </button>
52 ${h.end_form()}
61 ${h.end_form()}
@@ -28,7 +28,8 b''
28 ${h.secure_form(h.route_path('my_account_emails_delete'), request=request)}
28 ${h.secure_form(h.route_path('my_account_emails_delete'), request=request)}
29 ${h.hidden('del_email_id',em.email_id)}
29 ${h.hidden('del_email_id',em.email_id)}
30 <button class="btn btn-link btn-danger" type="submit" id="${'remove_email_%s'.format(em.email_id)}"
30 <button class="btn btn-link btn-danger" type="submit" id="${'remove_email_%s'.format(em.email_id)}"
31 onclick="return confirm('${_('Confirm to delete this email: {}').format(em.email)}');">
31 onclick="submitConfirm(event, this, _gettext('Confirm to delete this email'), _gettext('Delete'), '${em.email}')"
32 >
32 ${_('Delete')}
33 ${_('Delete')}
33 </button>
34 </button>
34 ${h.end_form()}
35 ${h.end_form()}
@@ -2,11 +2,29 b''
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-body">
4 <div class="panel-body">
5 %if c.closed:
5 <div style="height: 35px">
6 ${h.checkbox('show_closed',checked="checked", label=_('Show Closed Pull Requests'))}
6 <%
7 %else:
7 selected_filter = 'all'
8 ${h.checkbox('show_closed',label=_('Show Closed Pull Requests'))}
8 if c.closed:
9 %endif
9 selected_filter = 'all_closed'
10 %>
11
12 <ul class="button-links">
13 <li class="btn ${h.is_active('all', selected_filter)}"><a href="${h.route_path('my_account_pullrequests')}">${_('All')}</a></li>
14 <li class="btn ${h.is_active('all_closed', selected_filter)}"><a href="${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}">${_('All + Closed')}</a></li>
15 </ul>
16
17 <div class="grid-quick-filter">
18 <ul class="grid-filter-box">
19 <li class="grid-filter-box-icon">
20 <i class="icon-search"></i>
21 </li>
22 <li class="grid-filter-box-input">
23 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
24 </li>
25 </ul>
26 </div>
27 </div>
10 </div>
28 </div>
11 </div>
29 </div>
12
30
@@ -20,72 +38,107 b''
20 </div>
38 </div>
21
39
22 <script type="text/javascript">
40 <script type="text/javascript">
23 $(document).ready(function() {
41 $(document).ready(function () {
24
42
25 $('#show_closed').on('click', function(e){
43 var $pullRequestListTable = $('#pull_request_list_table');
26 if($(this).is(":checked")){
27 window.location = "${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}";
28 }
29 else{
30 window.location = "${h.route_path('my_account_pullrequests')}";
31 }
32 });
33
34 var $pullRequestListTable = $('#pull_request_list_table');
35
44
36 // participating object list
45 // participating object list
37 $pullRequestListTable.DataTable({
46 $pullRequestListTable.DataTable({
38 processing: true,
47 processing: true,
39 serverSide: true,
48 serverSide: true,
40 ajax: {
49 ajax: {
41 "url": "${h.route_path('my_account_pullrequests_data')}",
50 "url": "${h.route_path('my_account_pullrequests_data')}",
42 "data": function (d) {
51 "data": function (d) {
43 d.closed = "${c.closed}";
52 d.closed = "${c.closed}";
44 }
53 },
45 },
54 "dataSrc": function (json) {
46 dom: 'rtp',
55 return json.data;
47 pageLength: ${c.visual.dashboard_items},
56 }
48 order: [[ 2, "desc" ]],
57 },
49 columns: [
58
50 { data: {"_": "target_repo",
59 dom: 'rtp',
51 "sort": "target_repo"}, title: "${_('Target Repo')}", className: "td-targetrepo", orderable: false},
60 pageLength: ${c.visual.dashboard_items},
52 { data: {"_": "status",
61 order: [[2, "desc"]],
53 "sort": "status"}, title: "", className: "td-status", orderable: false},
62 columns: [
54 { data: {"_": "name",
63 {
55 "sort": "name_raw"}, title: "${_('Id')}", className: "td-componentname", "type": "num" },
64 data: {
56 { data: {"_": "title",
65 "_": "target_repo",
57 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
66 "sort": "target_repo"
58 { data: {"_": "author",
67 }, title: "${_('Target Repo')}", className: "td-targetrepo", orderable: false
59 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
68 },
60 { data: {"_": "comments",
69 {
61 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
70 data: {
62 { data: {"_": "updated_on",
71 "_": "status",
63 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
72 "sort": "status"
64 ],
73 }, title: "", className: "td-status", orderable: false
65 language: {
74 },
75 {
76 data: {
77 "_": "name",
78 "sort": "name_raw"
79 }, title: "${_('Id')}", className: "td-componentname", "type": "num"
80 },
81 {
82 data: {
83 "_": "title",
84 "sort": "title"
85 }, title: "${_('Title')}", className: "td-description"
86 },
87 {
88 data: {
89 "_": "author",
90 "sort": "author_raw"
91 }, title: "${_('Author')}", className: "td-user", orderable: false
92 },
93 {
94 data: {
95 "_": "comments",
96 "sort": "comments_raw"
97 }, title: "", className: "td-comments", orderable: false
98 },
99 {
100 data: {
101 "_": "updated_on",
102 "sort": "updated_on_raw"
103 }, title: "${_('Last Update')}", className: "td-time"
104 }
105 ],
106 language: {
66 paginate: DEFAULT_GRID_PAGINATION,
107 paginate: DEFAULT_GRID_PAGINATION,
67 sProcessing: _gettext('loading...'),
108 sProcessing: _gettext('loading...'),
68 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
109 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
69 },
110 },
70 "drawCallback": function( settings, json ) {
111 "drawCallback": function (settings, json) {
71 timeagoActivate();
112 timeagoActivate();
72 tooltipActivate();
113 tooltipActivate();
73 },
114 },
74 "createdRow": function ( row, data, index ) {
115 "createdRow": function (row, data, index) {
75 if (data['closed']) {
116 if (data['closed']) {
76 $(row).addClass('closed');
117 $(row).addClass('closed');
77 }
118 }
78 if (data['owned']) {
119 if (data['owned']) {
79 $(row).addClass('owned');
120 $(row).addClass('owned');
80 }
121 }
81 }
122 }
82 });
123 });
83 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
124 $pullRequestListTable.on('xhr.dt', function (e, settings, json, xhr) {
84 $pullRequestListTable.css('opacity', 1);
125 $pullRequestListTable.css('opacity', 1);
85 });
126 });
86
127
87 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
128 $pullRequestListTable.on('preXhr.dt', function (e, settings, data) {
88 $pullRequestListTable.css('opacity', 0.3);
129 $pullRequestListTable.css('opacity', 0.3);
89 });
130 });
131
132 // filter
133 $('#q_filter').on('keyup',
134 $.debounce(250, function () {
135 $pullRequestListTable.DataTable().search(
136 $('#q_filter').val()
137 ).draw();
138 })
139 );
140
90 });
141 });
142
143
91 </script>
144 </script>
@@ -29,7 +29,8 b''
29 ${h.secure_form(h.route_path('my_account_ssh_keys_delete'), request=request)}
29 ${h.secure_form(h.route_path('my_account_ssh_keys_delete'), request=request)}
30 ${h.hidden('del_ssh_key', ssh_key.ssh_key_id)}
30 ${h.hidden('del_ssh_key', ssh_key.ssh_key_id)}
31 <button class="btn btn-link btn-danger" type="submit"
31 <button class="btn btn-link btn-danger" type="submit"
32 onclick="return confirm('${_('Confirm to remove ssh key %s') % ssh_key.ssh_key_fingerprint}');">
32 onclick="submitConfirm(event, this, _gettext('Confirm to delete this ssh key'), _gettext('Delete'), '${ssh_key.ssh_key_fingerprint}')"
33 >
33 ${_('Delete')}
34 ${_('Delete')}
34 </button>
35 </button>
35 ${h.end_form()}
36 ${h.end_form()}
@@ -52,12 +52,29 b''
52 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
52 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
53 </div>
53 </div>
54 </div>
54 </div>
55
55 <div class="field">
56 <div class="field">
56 <div class="label">
57 <div class="label">
57 <label for="repo_type">${_('Type')}:</label>
58 <label for="repo_type">${_('Type')}:</label>
58 </div>
59 </div>
59 <div class="select">
60 <div class="fields repo-type-radio">
60 ${h.select('repo_type','hg',c.backends)}
61
62
63 % for backend in c.backends:
64 % if loop.index == 0:
65 <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" checked="checked"/>
66 % else:
67 <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" />
68 % endif
69
70 <label for="repo_type_${backend}">
71 <i class="icon-${backend}" style="font-size: 16px"></i>
72 ${backend.upper()}
73 </label>
74
75 % endfor
76
77
61 <span class="help-block">${_('Set the type of repository to create.')}</span>
78 <span class="help-block">${_('Set the type of repository to create.')}</span>
62 </div>
79 </div>
63 </div>
80 </div>
@@ -140,12 +157,6 b''
140 setCopyPermsOption(e.val)
157 setCopyPermsOption(e.val)
141 });
158 });
142
159
143 $("#repo_type").select2({
144 'containerCssClass': "drop-menu",
145 'dropdownCssClass': "drop-menu-dropdown",
146 'minimumResultsForSearch': -1,
147 });
148
149 $('#repo_name').focus();
160 $('#repo_name').focus();
150
161
151 $('#select_my_group').on('click', function(e){
162 $('#select_my_group').on('click', function(e){
@@ -93,14 +93,16 b''
93 %if c.rhodecode_db_repo.locked[0]:
93 %if c.rhodecode_db_repo.locked[0]:
94 ${h.hidden('set_unlock', '1')}
94 ${h.hidden('set_unlock', '1')}
95 <button class="btn btn-small" type="submit"
95 <button class="btn btn-small" type="submit"
96 onclick="return confirm('${_('Confirm to unlock repository.')}');">
96 onclick="submitConfirm(event, this, _gettext('Confirm to unlock this repository'), _gettext('Unlock'), '${c.rhodecode_db_repo.repo_name}')"
97 >
97 <i class="icon-unlock"></i>
98 <i class="icon-unlock"></i>
98 ${_('Unlock repository')}
99 ${_('Unlock repository')}
99 </button>
100 </button>
100 %else:
101 %else:
101 ${h.hidden('set_lock', '1')}
102 ${h.hidden('set_lock', '1')}
102 <button class="btn btn-small" type="submit"
103 <button class="btn btn-small" type="submit"
103 onclick="return confirm('${_('Confirm to lock repository.')}');">
104 onclick="submitConfirm(event, this, _gettext('Confirm to lock this repository'), _gettext('lock'), '${c.rhodecode_db_repo.repo_name}')"
105 >
104 <i class="icon-lock"></i>
106 <i class="icon-lock"></i>
105 ${_('Lock repository')}
107 ${_('Lock repository')}
106 </button>
108 </button>
@@ -147,6 +149,11 b''
147 onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');">
149 onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');">
148 ${_('Update Hooks')}
150 ${_('Update Hooks')}
149 </a>
151 </a>
152 % if c.hooks_outdated:
153 <span class="alert-error" style="padding: 10px">
154 ${_('Outdated hooks detected, please update hooks using `Update Hooks` action.')}
155 </span>
156 % endif
150 </div>
157 </div>
151 </div>
158 </div>
152
159
@@ -161,7 +168,8 b''
161
168
162 <div class="field">
169 <div class="field">
163 <button class="btn btn-small btn-warning" type="submit"
170 <button class="btn btn-small btn-warning" type="submit"
164 onclick="return confirm('${_('Confirm to archive this repository: %s') % c.repo_name}');">
171 onclick="submitConfirm(event, this, _gettext('Confirm to archive this repository'), _gettext('Archive'), '${c.rhodecode_db_repo.repo_name}')"
172 >
165 ${_('Archive this repository')}
173 ${_('Archive this repository')}
166 </button>
174 </button>
167 </div>
175 </div>
@@ -216,7 +224,8 b''
216
224
217 <div class="field">
225 <div class="field">
218 <button class="btn btn-small btn-danger" type="submit"
226 <button class="btn btn-small btn-danger" type="submit"
219 onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');">
227 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${c.rhodecode_db_repo.repo_name}')"
228 >
220 ${_('Delete this repository')}
229 ${_('Delete this repository')}
221 </button>
230 </button>
222 </div>
231 </div>
@@ -17,7 +17,11 b''
17 ${h.secure_form(h.route_path('edit_repo_caches', repo_name=c.repo_name), request=request)}
17 ${h.secure_form(h.route_path('edit_repo_caches', repo_name=c.repo_name), request=request)}
18 <div class="form">
18 <div class="form">
19 <div class="fields">
19 <div class="fields">
20 ${h.submit('reset_cache_%s' % c.rhodecode_db_repo.repo_name,_('Invalidate repository cache'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
20 <input class="btn btn-small" id="reset_cache_${c.rhodecode_db_repo.repo_id}"
21 name="reset_cache_${c.rhodecode_db_repo.repo_id}"
22 onclick="submitConfirm(event, this, _gettext('Confirm to invalidate repository cache'), _gettext('Invalidate Cache'), '${c.rhodecode_db_repo.repo_name}')"
23 type="submit" value="${_('Invalidate repository cache')}"
24 >
21 </div>
25 </div>
22 </div>
26 </div>
23 ${h.end_form()}
27 ${h.end_form()}
@@ -59,7 +59,7 b''
59 ${base.gravatar(h.DEFAULT_USER_EMAIL, 16)}
59 ${base.gravatar(h.DEFAULT_USER_EMAIL, 16)}
60 ${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')}</td>
60 ${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')}</td>
61 <td class="td-action">
61 <td class="td-action">
62 <span class="tooltip btn btn-link btn-default" onclick="setPrivateRepo(false); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
62 <span class="noselect tooltip btn btn-link btn-default" onclick="setPrivateRepo(this, false); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
63 ${_('un-set private mode')}
63 ${_('un-set private mode')}
64 </span>
64 </span>
65 </td>
65 </td>
@@ -115,7 +115,7 b''
115 ${_('Remove')}
115 ${_('Remove')}
116 </span>
116 </span>
117 %elif _user.username == h.DEFAULT_USER:
117 %elif _user.username == h.DEFAULT_USER:
118 <span class="tooltip btn btn-link btn-default" onclick="setPrivateRepo(true); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
118 <span class="noselect tooltip btn btn-link btn-default" onclick="setPrivateRepo(this, true); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
119 ${_('set private mode')}
119 ${_('set private mode')}
120 </span>
120 </span>
121 %endif
121 %endif
@@ -213,7 +213,14 b''
213 });
213 });
214 quick_repo_menu();
214 quick_repo_menu();
215
215
216 var setPrivateRepo = function (private) {
216 var setPrivateRepo = function (elem, private) {
217 var $elem = $(elem)
218 if ($elem.hasClass('disabled')) {
219 return
220 }
221 $elem.addClass('disabled');
222 $elem.css({"opacity": 0.3})
223
217 var postData = {
224 var postData = {
218 'csrf_token': CSRF_TOKEN,
225 'csrf_token': CSRF_TOKEN,
219 'private': private
226 'private': private
@@ -37,7 +37,12 b''
37 ${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), request=request)}
37 ${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), request=request)}
38 <div class="form">
38 <div class="form">
39 <div class="fields">
39 <div class="fields">
40 ${h.submit('remote_pull_%s' % c.rhodecode_db_repo.repo_name,_('Pull changes from remote location'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
40 <input class="btn btn-small"
41 id="remote_pull_${c.rhodecode_db_repo.repo_id}"
42 name="remote_pull_${c.rhodecode_db_repo.repo_id}"
43 onclick="submitConfirm(event, this, _gettext('Confirm pull changes from remote side'), _gettext('Pull changes'), '${c.rhodecode_db_repo.clone_uri_hidden}')"
44 type="submit" value="${_('Pull changes from remote location')}"
45 >
41 </div>
46 </div>
42 </div>
47 </div>
43 ${h.end_form()}
48 ${h.end_form()}
@@ -6,8 +6,17 b''
6 % if c.traceback:
6 % if c.traceback:
7
7
8 <h4>${_('Exception `{}` generated on UTC date: {}').format(c.traceback.get('exc_type', 'NO_TYPE'), c.traceback.get('exc_utc_date', 'NO_DATE'))}</h4>
8 <h4>${_('Exception `{}` generated on UTC date: {}').format(c.traceback.get('exc_type', 'NO_TYPE'), c.traceback.get('exc_utc_date', 'NO_DATE'))}</h4>
9 % if c.traceback.get('url'):
10 Request:
11 <code>${c.traceback.get('method')} ${c.traceback.get('url')}</code><br/>
12 <code>${c.traceback.get('client_address')} ${c.traceback.get('user_agent')}</code>
13 <br/>
14 <br/>
15 % endif
16
9 <pre>${c.traceback.get('exc_message', 'NO_MESSAGE')}</pre>
17 <pre>${c.traceback.get('exc_message', 'NO_MESSAGE')}</pre>
10
18
19
11 % else:
20 % else:
12 ${_('Unable to Read Exception. It might be removed or non-existing.')}
21 ${_('Unable to Read Exception. It might be removed or non-existing.')}
13 % endif
22 % endif
@@ -26,7 +35,7 b''
26
35
27 <div class="field">
36 <div class="field">
28 <button class="btn btn-small btn-danger" type="submit"
37 <button class="btn btn-small btn-danger" type="submit"
29 onclick="return confirm('${_('Confirm to delete this exception')}');">
38 onclick="submitConfirm(event, this, _gettext('Confirm to delete this exception'), _gettext('Delete'), ${c.exception_id})">
30 <i class="icon-remove"></i>
39 <i class="icon-remove"></i>
31 ${_('Delete This Exception')}
40 ${_('Delete This Exception')}
32 </button>
41 </button>
@@ -16,7 +16,8 b''
16 <input type="hidden" name="type_filter", value="${c.type_filter}">
16 <input type="hidden" name="type_filter", value="${c.type_filter}">
17 <div class="field">
17 <div class="field">
18 <button class="btn btn-small btn-danger" type="submit"
18 <button class="btn btn-small btn-danger" type="submit"
19 onclick="return confirm('${_('Confirm to delete all exceptions')}');">
19 onclick="submitConfirm(event, this, _gettext('Confirm to delete all exceptions'), _gettext('Delete'), '${'total:{}'.format(c.exception_list_count)}')"
20 >
20 <i class="icon-remove"></i>
21 <i class="icon-remove"></i>
21 % if c.type_filter:
22 % if c.type_filter:
22 ${_('Delete All `{}`').format(c.type_filter)}
23 ${_('Delete All `{}`').format(c.type_filter)}
@@ -46,12 +46,19 b''
46 <h3 class="panel-title">${_('Python Packages')}</h3>
46 <h3 class="panel-title">${_('Python Packages')}</h3>
47 </div>
47 </div>
48 <div class="panel-body">
48 <div class="panel-body">
49 <dl class="dl-horizontal settings dt-400">
49 <table>
50 % for dt, dd in c.py_modules['human_value']:
50 <th></th>
51 <dt>${dt}${':' if dt else '---'}</dt>
51 <th></th>
52 <dd>${dd}${'' if dt else '---'}</dd>
52 <th></th>
53 % endfor
53 % for name, package_data in c.py_modules['human_value']:
54 </dl>
54 <tr>
55 <td>${name.lower()}</td>
56 <td>${package_data['version']}</td>
57 <td>(${package_data['location']})</td>
58 </tr>
59 % endfor
60 </table>
61
55 </div>
62 </div>
56 </div>
63 </div>
57
64
@@ -40,8 +40,8 b' SYSTEM SETTINGS'
40 PYTHON PACKAGES
40 PYTHON PACKAGES
41 ---------------
41 ---------------
42
42
43 % for key, value in c.py_modules['human_value']:
43 % for name, package_data in c.py_modules['human_value']:
44 ${'{:<60}'.format(key)}: ${value}
44 ${'{:<60}'.format(name)}: ${'{:<20}'.format(package_data['version'])} (${package_data['location']})
45 % endfor
45 % endfor
46
46
47 </pre>
47 </pre>
@@ -181,6 +181,7 b''
181 } else if (suggestion.value_type == 'user') {
181 } else if (suggestion.value_type == 'user') {
182 addMember(suggestion, null);
182 addMember(suggestion, null);
183 }
183 }
184 $('#user_group_add_members').val('')
184 }
185 }
185 });
186 });
186
187
@@ -149,6 +149,18 b''
149
149
150 <tr>
150 <tr>
151 <td>
151 <td>
152 ${_ungettext('This user owns %s pull request.', 'This user owns %s pull requests.', len(c.user.user_pull_requests)) % len(c.user.user_pull_requests)}
153 </td>
154 <td>
155 <input type="radio" id="user_pull_requests_1" name="user_pull_requests" value="detach" checked="checked" ${'disabled=1' if len(c.user.user_pull_requests) == 0 else ''}/> <label for="user_pull_requests_1">${_('Detach pull requests')}</label>
156 </td>
157 <td>
158 <input type="radio" id="user_pull_requests_2" name="user_pull_requests" value="delete" ${'disabled=1' if len(c.user.user_pull_requests) == 0 else ''}/> <label for="user_pull_requests_2">${_('Delete pull requests')}</label>
159 </td>
160 </tr>
161
162 <tr>
163 <td>
152 ${_ungettext('This user owns %s artifact.', 'This user owns %s artifacts.', len(c.user.artifacts)) % len(c.user.artifacts)}
164 ${_ungettext('This user owns %s artifact.', 'This user owns %s artifacts.', len(c.user.artifacts)) % len(c.user.artifacts)}
153 </td>
165 </td>
154 <td>
166 <td>
@@ -166,7 +178,8 b''
166 % endif
178 % endif
167
179
168 <span style="padding: 0 5px 0 0">${_('New owner for detached objects')}:</span>
180 <span style="padding: 0 5px 0 0">${_('New owner for detached objects')}:</span>
169 <div class="pull-right">${base.gravatar_with_user(c.first_admin.email, 16)}</div>
181 <div class="pull-right">${base.gravatar_with_user(c.detach_user.email, 16, tooltip=True)}</div>
182 <input type="hidden" name="detach_user_id" value="${c.detach_user.user_id}">
170 </div>
183 </div>
171 <div style="clear: both">
184 <div style="clear: both">
172
185
@@ -186,11 +199,11 b''
186 <div style="margin: 0 0 20px 0" class="fake-space"></div>
199 <div style="margin: 0 0 20px 0" class="fake-space"></div>
187
200
188 <div class="field">
201 <div class="field">
189 <button class="btn btn-small btn-danger" type="submit"
202 <input class="btn btn-small btn-danger" id="remove_user" name="remove_user"
190 onclick="return confirm('${_('Confirm to delete this user: %s') % c.user.username}');"
203 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Confirm Delete'), '${c.user.username}')"
191 ${"disabled" if not c.can_delete_user else ""}>
204 ${("disabled=1" if not c.can_delete_user else "")}
192 ${_('Delete this user')}
205 type="submit" value="${_('Delete this user')}"
193 </button>
206 >
194 </div>
207 </div>
195
208
196 ${h.end_form()}
209 ${h.end_form()}
@@ -1,6 +1,12 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <script>
5 var showAuthToken = function(authTokenId) {
6 return _showAuthToken(authTokenId, pyroutes.url('edit_user_auth_tokens_view', {'user_id': '${c.user.user_id}'}))
7 }
8 </script>
9
4 <div class="panel-heading">
10 <div class="panel-heading">
5 <h3 class="panel-title">
11 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
12 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
@@ -26,9 +32,11 b''
26 %if c.user_auth_tokens:
32 %if c.user_auth_tokens:
27 %for auth_token in c.user_auth_tokens:
33 %for auth_token in c.user_auth_tokens:
28 <tr class="${('expired' if auth_token.expired else '')}">
34 <tr class="${('expired' if auth_token.expired else '')}">
29 <td class="truncate-wrap td-authtoken">
35 <td class="td-authtoken">
30 <div class="user_auth_tokens truncate autoexpand">
36 <div class="user_auth_tokens">
31 <code>${auth_token.api_key}</code>
37 <code class="cursor-pointer" onclick="showAuthToken(${auth_token.user_api_key_id})">
38 ${auth_token.token_obfuscated}
39 </code>
32 </div>
40 </div>
33 </td>
41 </td>
34 <td class="td-wrap">${auth_token.description}</td>
42 <td class="td-wrap">${auth_token.description}</td>
@@ -49,7 +57,7 b''
49 </td>
57 </td>
50 <td class="td-action">
58 <td class="td-action">
51 ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), request=request)}
59 ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), request=request)}
52 ${h.hidden('del_auth_token', auth_token.user_api_key_id)}
60 <input name="del_auth_token" type="hidden" value="${auth_token.user_api_key_id}">
53 <button class="btn btn-link btn-danger" type="submit"
61 <button class="btn btn-link btn-danger" type="submit"
54 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');">
62 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');">
55 ${_('Delete')}
63 ${_('Delete')}
@@ -688,17 +688,50 b''
688 </%def>
688 </%def>
689
689
690 <%def name="menu_items(active=None)">
690 <%def name="menu_items(active=None)">
691 <%
692 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
693 notice_display = 'none' if len(notice_messages) == 0 else ''
694 %>
695 <style>
696
697 </style>
691
698
692 <ul id="quick" class="main_nav navigation horizontal-list">
699 <ul id="quick" class="main_nav navigation horizontal-list">
693 ## notice box for important system messages
700 ## notice box for important system messages
694 <li style="display: none">
701 <li style="display: ${notice_display}">
695 <a class="notice-box" href="#openNotice" onclick="return false">
702 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
696 <div class="menulabel-notice" >
703 <div class="menulabel-notice ${notice_level}" >
697 0
704 ${len(notice_messages)}
698 </div>
705 </div>
699 </a>
706 </a>
700 </li>
707 </li>
708 <div class="notice-messages-container" style="display: none">
709 <div class="notice-messages">
710 <table class="rctable">
711 % for notice in notice_messages:
712 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
713 <td style="vertical-align: text-top; width: 20px">
714 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
715 </td>
716 <td>
717 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
718 ${notice['subject']}
701
719
720 <div id="notice-${notice['msg_id']}" style="display: none">
721 ${h.render(notice['body'], renderer='markdown')}
722 </div>
723 </td>
724 <td style="vertical-align: text-top; width: 35px;">
725 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
726 <i class="icon-remove icon-filled-red"></i>
727 </a>
728 </td>
729 </tr>
730
731 % endfor
732 </table>
733 </div>
734 </div>
702 ## Main filter
735 ## Main filter
703 <li>
736 <li>
704 <div class="menulabel main_filter_box">
737 <div class="menulabel main_filter_box">
@@ -750,6 +783,8 b''
750
783
751 user_group:devops, to search for user groups, always global
784 user_group:devops, to search for user groups, always global
752
785
786 pr:303, to search for pull request number, title, or description, always global
787
753 commit:efced4, to search for commits, scoped to repositories or groups
788 commit:efced4, to search for commits, scoped to repositories or groups
754
789
755 file:models.py, to search for file paths, scoped to repositories or groups
790 file:models.py, to search for file paths, scoped to repositories or groups
@@ -942,6 +977,10 b''
942 else if (searchType === 'user') {
977 else if (searchType === 'user') {
943 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
978 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
944 }
979 }
980 // pull request
981 else if (searchType === 'pull_request') {
982 icon += '<i class="icon-merge"></i> ';
983 }
945 // commit
984 // commit
946 else if (searchType === 'commit') {
985 else if (searchType === 'commit') {
947 var repo_data = data['repo_data'];
986 var repo_data = data['repo_data'];
@@ -1030,8 +1069,14 b''
1030 },
1069 },
1031 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1070 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1032 if (jqXHR !== 'abort') {
1071 if (jqXHR !== 'abort') {
1033 alert("Error during search.\nError code: {0}".format(textStatus));
1072 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1034 window.location = '';
1073 SwalNoAnimation.fire({
1074 icon: 'error',
1075 title: _gettext('Error during search operation'),
1076 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1077 }).then(function(result) {
1078 window.location.reload();
1079 })
1035 }
1080 }
1036 },
1081 },
1037 onSearchStart: function (params) {
1082 onSearchStart: function (params) {
@@ -1058,6 +1103,26 b''
1058 }
1103 }
1059 });
1104 });
1060
1105
1106 var dismissNotice = function(noticeId) {
1107
1108 var url = pyroutes.url('user_notice_dismiss',
1109 {"user_id": templateContext.rhodecode_user.user_id});
1110
1111 var postData = {
1112 'csrf_token': CSRF_TOKEN,
1113 'notice_id': noticeId,
1114 };
1115
1116 var success = function(response) {
1117 $('#notice-message-' + noticeId).remove();
1118 return false;
1119 };
1120 var failure = function(data, textStatus, xhr) {
1121 alert("error processing request: " + textStatus);
1122 return false;
1123 };
1124 ajaxPOST(url, postData, success, failure);
1125 }
1061 </script>
1126 </script>
1062 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1127 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1063 </%def>
1128 </%def>
@@ -15,6 +15,7 b" if getattr(c, 'repo_group', None):"
15 c.template_context['repo_group_name'] = c.repo_group.group_name
15 c.template_context['repo_group_name'] = c.repo_group.group_name
16
16
17 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
17 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
18 c.template_context['rhodecode_user']['user_id'] = c.rhodecode_user.user_id
18 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
19 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
19 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
20 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
20 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
21 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
@@ -135,7 +135,7 b''
135 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
135 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
136 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
136 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
137 ## permissions to delete
137 ## permissions to delete
138 %if c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
138 %if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
139 ## TODO: dan: add edit comment here
139 ## TODO: dan: add edit comment here
140 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
140 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
141 %else:
141 %else:
@@ -2,7 +2,7 b''
2
2
3 <script>
3 <script>
4 var CHANNELSTREAM_URLS = ${config['url_gen'](request)|n};
4 var CHANNELSTREAM_URLS = ${config['url_gen'](request)|n};
5 %if request.registry.rhodecode_plugins['channelstream']['enabled'] and c.rhodecode_user.username != h.DEFAULT_USER:
5 %if request.registry.rhodecode_plugins['channelstream']['enabled'] and getattr(c.rhodecode_user, 'username', '') != h.DEFAULT_USER:
6 var CHANNELSTREAM_SETTINGS = {
6 var CHANNELSTREAM_SETTINGS = {
7 'enabled': true,
7 'enabled': true,
8 'ws_location': '${request.registry.settings.get('channelstream.ws_url')}',
8 'ws_location': '${request.registry.settings.get('channelstream.ws_url')}',
@@ -15,5 +15,4 b''
15 'webapp_location': '${h.route_url('home').rstrip('/')}'
15 'webapp_location': '${h.route_url('home').rstrip('/')}'
16 };
16 };
17 %endif
17 %endif
18
19 </script>
18 </script>
@@ -394,11 +394,17 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
394 </span>
394 </span>
395 <span class="pill-group pull-left" >
395 <span class="pill-group pull-left" >
396 ## file op, doesn't need translation
396 ## file op, doesn't need translation
397 <span class="pill" op="removed">removed in this version</span>
397 <span class="pill" op="removed">unresolved comments</span>
398 </span>
398 </span>
399 <a class="pill filediff-anchor" href="#a_${h.FID(fid, filename)}"></a>
399 <a class="pill filediff-anchor" href="#a_${h.FID(fid, filename)}"></a>
400 <span class="pill-group pull-right">
400 <span class="pill-group pull-right">
401 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
401 <span class="pill" op="deleted">
402 % if comments_dict['stats'] >0:
403 -${comments_dict['stats']}
404 % else:
405 ${comments_dict['stats']}
406 % endif
407 </span>
402 </span>
408 </span>
403 </label>
409 </label>
404
410
@@ -410,7 +416,8 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
410
416
411 <td></td>
417 <td></td>
412 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
418 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
413 ${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
419 <strong>${_('This file was removed from diff during updates to this pull-request.')}</strong><br/>
420 ${_('There are still outdated/unresolved comments attached to it.')}
414 </td>
421 </td>
415 </tr>
422 </tr>
416 %if c.user_session_attrs["diffmode"] == 'unified':
423 %if c.user_session_attrs["diffmode"] == 'unified':
@@ -477,7 +484,7 b' from rhodecode.lib.diffs import NEW_FILE'
477 <% final_path = filediff.target_file_path %>
484 <% final_path = filediff.target_file_path %>
478 %endif
485 %endif
479 %endif
486 %endif
480 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
487 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy file path')}" onclick="return false;"></i>
481 </span>
488 </span>
482 ## anchor link
489 ## anchor link
483 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></a>
490 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></a>
@@ -2,10 +2,8 b''
2 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
3
3
4 %if c.ancestor:
4 %if c.ancestor:
5 <div class="ancestor">${_('Common Ancestor Commit')}:
5 <div class="ancestor">${_('Compare was calculated based on this common ancestor commit')}:
6 <a href="${h.route_path('repo_commit', repo_name=c.repo_name, commit_id=c.ancestor)}">
6 <a href="${h.route_path('repo_commit', repo_name=c.repo_name, commit_id=c.ancestor)}">${h.short_id(c.ancestor)}</a>
7 ${h.short_id(c.ancestor)}
8 </a>. ${_('Compare was calculated based on this shared commit.')}
9 <input id="common_ancestor" type="hidden" name="common_ancestor" value="${c.ancestor}">
7 <input id="common_ancestor" type="hidden" name="common_ancestor" value="${c.ancestor}">
10 </div>
8 </div>
11 %endif
9 %endif
@@ -176,8 +176,10 b''
176 </div>
176 </div>
177 <div class="grid_delete">
177 <div class="grid_delete">
178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
179 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
179 <input class="btn btn-link btn-danger" id="remove_${repo_name}" name="remove_${repo_name}"
180 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
180 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${repo_name}')"
181 type="submit" value="Delete"
182 >
181 ${h.end_form()}
183 ${h.end_form()}
182 </div>
184 </div>
183 </div>
185 </div>
@@ -250,8 +252,10 b''
250 </div>
252 </div>
251 <div class="grid_delete">
253 <div class="grid_delete">
252 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
254 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
253 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
255 <input class="btn btn-link btn-danger" id="remove_${repo_group_name}" name="remove_${repo_group_name}"
254 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
256 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository group'), _gettext('Delete'), '${_ungettext('`{}` with {} repository','`{}` with {} repositories',gr_count).format(repo_group_name, gr_count)}')"
257 type="submit" value="Delete"
258 >
255 ${h.end_form()}
259 ${h.end_form()}
256 </div>
260 </div>
257 </%def>
261 </%def>
@@ -265,8 +269,10 b''
265 </div>
269 </div>
266 <div class="grid_delete">
270 <div class="grid_delete">
267 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
271 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
268 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
272 <input class="btn btn-link btn-danger" id="remove_user_${user_id}" name="remove_user_${user_id}"
269 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
273 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Delete'), '${username}')"
274 type="submit" value="Delete"
275 >
270 ${h.end_form()}
276 ${h.end_form()}
271 </div>
277 </div>
272 </%def>
278 </%def>
@@ -277,8 +283,10 b''
277 </div>
283 </div>
278 <div class="grid_delete">
284 <div class="grid_delete">
279 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
285 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
280 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
286 <input class="btn btn-link btn-danger" id="remove_group_${user_group_id}" name="remove_group_${user_group_id}"
281 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
287 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user group'), _gettext('Delete'), '${user_group_name}')"
288 type="submit" value="Delete"
289 >
282 ${h.end_form()}
290 ${h.end_form()}
283 </div>
291 </div>
284 </%def>
292 </%def>
@@ -423,8 +431,10 b''
423 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
431 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
424 <div class="grid_delete">
432 <div class="grid_delete">
425 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
433 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
426 ${h.submit('remove_',_('Delete'),id="remove_artifact_%s" % file_store_id, class_="btn btn-link btn-danger",
434 <input class="btn btn-link btn-danger" id="remove_artifact_${file_store_id}" name="remove_artifact_${file_store_id}"
427 onclick="return confirm('"+_('Confirm to delete this artifact: %s') % file_uid+"');")}
435 onclick="submitConfirm(event, this, _gettext('Confirm to delete this artifact'), _gettext('Delete'), '${file_uid}')"
436 type="submit" value="${_('Delete')}"
437 >
428 ${h.end_form()}
438 ${h.end_form()}
429 </div>
439 </div>
430 % endif
440 % endif
@@ -484,21 +484,84 b' text_monospace = "\'Menlo\', \'Liberation M'
484 background-color: #F5F5F5
484 background-color: #F5F5F5
485 }
485 }
486
486
487 /*elasticmatch is custom rhodecode tag*/
488 .codehilite .c-ElasticMatch {
489 background-color: #faffa6;
490 padding: 0.2em;
491 }
492
493 .codehilite .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;}
494 .codehilite .hll { background-color: #ffffcc }
495 .codehilite .c { color: #408080; font-style: italic } /* Comment */
496 .codehilite .err { border: none } /* Error */
497 .codehilite .k { color: #008000; font-weight: bold } /* Keyword */
498 .codehilite .o { color: #666666 } /* Operator */
499 .codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
500 .codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
501 .codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
502 .codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
503 .codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
504 .codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
505 .codehilite .gd { color: #A00000 } /* Generic.Deleted */
506 .codehilite .ge { font-style: italic } /* Generic.Emph */
507 .codehilite .gr { color: #FF0000 } /* Generic.Error */
508 .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
509 .codehilite .gi { color: #00A000 } /* Generic.Inserted */
510 .codehilite .go { color: #888888 } /* Generic.Output */
511 .codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
512 .codehilite .gs { font-weight: bold } /* Generic.Strong */
513 .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
514 .codehilite .gt { color: #0044DD } /* Generic.Traceback */
515 .codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
516 .codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
517 .codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
518 .codehilite .kp { color: #008000 } /* Keyword.Pseudo */
519 .codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
520 .codehilite .kt { color: #B00040 } /* Keyword.Type */
521 .codehilite .m { color: #666666 } /* Literal.Number */
522 .codehilite .s { color: #BA2121 } /* Literal.String */
523 .codehilite .na { color: #7D9029 } /* Name.Attribute */
524 .codehilite .nb { color: #008000 } /* Name.Builtin */
525 .codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
526 .codehilite .no { color: #880000 } /* Name.Constant */
527 .codehilite .nd { color: #AA22FF } /* Name.Decorator */
528 .codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
529 .codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
530 .codehilite .nf { color: #0000FF } /* Name.Function */
531 .codehilite .nl { color: #A0A000 } /* Name.Label */
532 .codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
533 .codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
534 .codehilite .nv { color: #19177C } /* Name.Variable */
535 .codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
536 .codehilite .w { color: #bbbbbb } /* Text.Whitespace */
537 .codehilite .mb { color: #666666 } /* Literal.Number.Bin */
538 .codehilite .mf { color: #666666 } /* Literal.Number.Float */
539 .codehilite .mh { color: #666666 } /* Literal.Number.Hex */
540 .codehilite .mi { color: #666666 } /* Literal.Number.Integer */
541 .codehilite .mo { color: #666666 } /* Literal.Number.Oct */
542 .codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
543 .codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
544 .codehilite .sc { color: #BA2121 } /* Literal.String.Char */
545 .codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
546 .codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
547 .codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
548 .codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
549 .codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
550 .codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
551 .codehilite .sx { color: #008000 } /* Literal.String.Other */
552 .codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
553 .codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
554 .codehilite .ss { color: #19177C } /* Literal.String.Symbol */
555 .codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
556 .codehilite .fm { color: #0000FF } /* Name.Function.Magic */
557 .codehilite .vc { color: #19177C } /* Name.Variable.Class */
558 .codehilite .vg { color: #19177C } /* Name.Variable.Global */
559 .codehilite .vi { color: #19177C } /* Name.Variable.Instance */
560 .codehilite .vm { color: #19177C } /* Name.Variable.Magic */
561 .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
487
562
488 </style>
563 </style>
489
564
490 <!-- Targeting Windows Mobile -->
491 <!--[if IEMobile 7]>
492 <style type="text/css">
493
494 </style>
495 <![endif]-->
496
497 <!--[if gte mso 9]>
498 <style>
499 /* Target Outlook 2007 and 2010 */
500 </style>
501 <![endif]-->
502 </head>
565 </head>
503 <body>
566 <body>
504
567
@@ -21,6 +21,7 b''
21 // register templateContext to pass template variables to JS
21 // register templateContext to pass template variables to JS
22 var templateContext = {timeago: {}};
22 var templateContext = {timeago: {}};
23 </script>
23 </script>
24 <%include file="/base/plugins_base.mako"/>
24 <script type="text/javascript" src="${h.asset('js/scripts.min.js', ver=c.rhodecode_version_hash)}"></script>
25 <script type="text/javascript" src="${h.asset('js/scripts.min.js', ver=c.rhodecode_version_hash)}"></script>
25 </head>
26 </head>
26 <body>
27 <body>
@@ -1,10 +1,25 b''
1 <%def name="refs(commit)">
1 <%def name="refs(commit, at_rev=None)">
2 ## Build a cache of refs for selector
2
3 ## Build a cache of refs for selector, based on this the files ref selector gets pre-selected values
3 <script>
4 <script>
4 fileTreeRefs = {
5 fileTreeRefs = {}
6 </script>
5
7
6 }
8 % if h.is_svn(c.rhodecode_repo):
7 </script>
9 ## since SVN doesn't have an commit<->refs association, we simply inject it
10 ## based on our at_rev marker
11 % if at_rev and at_rev.startswith('branches/'):
12 <%
13 commit.branch = at_rev
14 %>
15 % endif
16 % if at_rev and at_rev.startswith('tags/'):
17 <%
18 commit.tags.append(at_rev)
19 %>
20 % endif
21
22 % endif
8
23
9 %if commit.merge:
24 %if commit.merge:
10 <span class="mergetag tag">
25 <span class="mergetag tag">
@@ -158,18 +158,24 b''
158
158
159 select2FileHistorySwitcher('#file_refs_filter', loadUrl, initialCommitData);
159 select2FileHistorySwitcher('#file_refs_filter', loadUrl, initialCommitData);
160
160
161 // switcher for files
161 $('#file_refs_filter').on('change', function(e) {
162 $('#file_refs_filter').on('change', function(e) {
162 var data = $('#file_refs_filter').select2('data');
163 var data = $('#file_refs_filter').select2('data');
163 var commit_id = data.id;
164 var commit_id = data.id;
165 var params = {
166 'repo_name': templateContext.repo_name,
167 'commit_id': commit_id,
168 'f_path': state.f_path
169 };
170
171 if(data.at_rev !== undefined && data.at_rev !== "") {
172 params['at'] = data.at_rev;
173 }
164
174
165 if ("${c.annotate}" === "True") {
175 if ("${c.annotate}" === "True") {
166 var url = pyroutes.url('repo_files:annotated',
176 var url = pyroutes.url('repo_files:annotated', params);
167 {'repo_name': templateContext.repo_name,
168 'commit_id': commit_id, 'f_path': state.f_path});
169 } else {
177 } else {
170 var url = pyroutes.url('repo_files',
178 var url = pyroutes.url('repo_files', params);
171 {'repo_name': templateContext.repo_name,
172 'commit_id': commit_id, 'f_path': state.f_path});
173 }
179 }
174 window.location = url;
180 window.location = url;
175
181
@@ -334,6 +340,7 b''
334
340
335 select2RefFileSwitcher('#refs_filter', loadUrl, initialCommitData);
341 select2RefFileSwitcher('#refs_filter', loadUrl, initialCommitData);
336
342
343 // switcher for file tree
337 $('#refs_filter').on('change', function(e) {
344 $('#refs_filter').on('change', function(e) {
338 var data = $('#refs_filter').select2('data');
345 var data = $('#refs_filter').select2('data');
339 window.location = data.files_url
346 window.location = data.files_url
@@ -41,7 +41,7 b''
41 </div>
41 </div>
42 </li>
42 </li>
43 <li class="location-path">
43 <li class="location-path">
44 <input class="file-name-input input-small" type="text" value="" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
44 <input class="file-name-input input-small" type="text" value="${request.GET.get('filename')}" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
45 </li>
45 </li>
46 </ul>
46 </ul>
47 </div>
47 </div>
@@ -50,7 +50,6 b''
50
50
51 <div class="table">
51 <div class="table">
52 <div>
52 <div>
53
54 <div id="codeblock" class="codeblock">
53 <div id="codeblock" class="codeblock">
55 <div class="editor-items">
54 <div class="editor-items">
56 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
55 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
@@ -108,6 +107,10 b''
108
107
109 $('#filename').focus();
108 $('#filename').focus();
110
109
110 var commit_id = "${c.commit.raw_id}";
111 var f_path = "${c.f_path}";
112
113 checkFileHead($('#eform'), commit_id, f_path, 'create')
111 });
114 });
112
115
113 </script>
116 </script>
@@ -41,7 +41,6 b''
41 % endif
41 % endif
42 </span>
42 </span>
43 % else:
43 % else:
44
45 <a href="${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=h.safe_unicode(node.path), _query=query)}">
44 <a href="${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=h.safe_unicode(node.path), _query=query)}">
46 <i class="${('icon-file-text browser-file' if node.is_file() else 'icon-directory browser-dir')}"></i>${node.name}
45 <i class="${('icon-file-text browser-file' if node.is_file() else 'icon-directory browser-dir')}"></i>${node.name}
47 </a>
46 </a>
@@ -69,7 +69,7 b''
69 </div>
69 </div>
70 </div>
70 </div>
71 <div class="pull-left">
71 <div class="pull-left">
72 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-danger-action")}
72 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-danger-action")}
73 </div>
73 </div>
74 </div>
74 </div>
75 ${h.end_form()}
75 ${h.end_form()}
@@ -82,6 +82,10 b''
82
82
83 fileEditor = new FileEditor('#editor');
83 fileEditor = new FileEditor('#editor');
84
84
85 var commit_id = "${c.commit.raw_id}";
86 var f_path = "${c.f_path}";
87
88 checkFileHead($('#eform'), commit_id, f_path, 'delete');
85 });
89 });
86
90
87 </script>
91 </script>
@@ -117,6 +117,11 b''
117 // on entering the new filename set mode, from given extension
117 // on entering the new filename set mode, from given extension
118 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
118 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
119
119
120 var commit_id = "${c.commit.raw_id}";
121 var f_path = "${c.f_path}";
122
123 checkFileHead($('#eform'), commit_id, f_path, 'edit')
124
120 });
125 });
121
126
122
127
@@ -104,6 +104,7 b''
104 | ${h.link_to(_('Raw'), h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
104 | ${h.link_to(_('Raw'), h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
105 % if not c.file.is_binary:
105 % if not c.file.is_binary:
106 |<a href="#copySource" onclick="return false;" class="no-grey clipboard-action" data-clipboard-text="${c.file.content}">${_('Copy content')}</a>
106 |<a href="#copySource" onclick="return false;" class="no-grey clipboard-action" data-clipboard-text="${c.file.content}">${_('Copy content')}</a>
107 |<a href="#copySource" onclick="return false;" class="no-grey clipboard-action" data-clipboard-text="${h.route_url('repo_files', repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">${_('Copy permalink')}</a>
107 % endif
108 % endif
108
109
109 </div>
110 </div>
@@ -114,7 +115,9 b''
114 %if c.file.is_binary:
115 %if c.file.is_binary:
115 <% rendered_binary = h.render_binary(c.repo_name, c.file)%>
116 <% rendered_binary = h.render_binary(c.repo_name, c.file)%>
116 % if rendered_binary:
117 % if rendered_binary:
118 <div class="text-center">
117 ${rendered_binary}
119 ${rendered_binary}
120 </div>
118 % else:
121 % else:
119 <div>
122 <div>
120 ${_('Binary file ({})').format(c.file.mimetype)}
123 ${_('Binary file ({})').format(c.file.mimetype)}
@@ -33,7 +33,7 b''
33 <p>${_('File last commit')}:</p>
33 <p>${_('File last commit')}:</p>
34 <div class="right-label-summary">
34 <div class="right-label-summary">
35 <code><a href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.file_last_commit.raw_id)}">${h.show_id(c.file_last_commit)}</a></code>
35 <code><a href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.file_last_commit.raw_id)}">${h.show_id(c.file_last_commit)}</a></code>
36 ${file_base.refs(c.file_last_commit)}
36 ${file_base.refs(c.file_last_commit)}
37 </div>
37 </div>
38 </div>
38 </div>
39 </div>
39 </div>
@@ -46,7 +46,7 b''
46 <a href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.commit.raw_id)}">${h.show_id(c.commit)}</a>
46 <a href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.commit.raw_id)}">${h.show_id(c.commit)}</a>
47 </code>
47 </code>
48 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.commit.raw_id}" title="${_('Copy the full commit id')}"></i>
48 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.commit.raw_id}" title="${_('Copy the full commit id')}"></i>
49 ${file_base.refs(c.commit)}
49 ${file_base.refs(c.commit, request.GET.get('at'))}
50 </div>
50 </div>
51 </div>
51 </div>
52 </div>
52 </div>
@@ -33,7 +33,8 b''
33 </code>
33 </code>
34
34
35 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.commit.raw_id}" title="${_('Copy the full commit id')}"></i>
35 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.commit.raw_id}" title="${_('Copy the full commit id')}"></i>
36 ${file_base.refs(c.commit)}
36 ${file_base.refs(c.commit, request.GET.get('at'))}
37
37 </div>
38 </div>
38 </div>
39 </div>
39
40
@@ -106,7 +106,7 b''
106
106
107 <div class="file-upload-transaction-wrapper" style="display: none">
107 <div class="file-upload-transaction-wrapper" style="display: none">
108 <div class="file-upload-transaction">
108 <div class="file-upload-transaction">
109 <h3>${_('Commiting...')}</h3>
109 <h3>${_('Committing...')}</h3>
110 <p>${_('Please wait while the files are being uploaded')}</p>
110 <p>${_('Please wait while the files are being uploaded')}</p>
111 <p class="error" style="display: none">
111 <p class="error" style="display: none">
112
112
@@ -22,4 +22,4 b''
22 </div>
22 </div>
23 </div>
23 </div>
24 ${field.end_sequence()}
24 ${field.end_sequence()}
25 </div> No newline at end of file
25 </div>
@@ -241,7 +241,93 b''
241 // custom code mirror
241 // custom code mirror
242 var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm;
242 var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm;
243
243
244 var diffDataHandler = function(data) {
245
246 $('#pull_request_overview').html(data);
247
248 var commitElements = data['commits'];
249 var files = data['files'];
250 var added = data['stats'][0]
251 var deleted = data['stats'][1]
252 var commonAncestorId = data['ancestor'];
253
254 var prTitleAndDesc = getTitleAndDescription(
255 sourceRef()[1], commitElements, 5);
256
257 var title = prTitleAndDesc[0];
258 var proposedDescription = prTitleAndDesc[1];
259
260 var useGeneratedTitle = (
261 $('#pullrequest_title').hasClass('autogenerated-title') ||
262 $('#pullrequest_title').val() === "");
263
264 if (title && useGeneratedTitle) {
265 // use generated title if we haven't specified our own
266 $('#pullrequest_title').val(title);
267 $('#pullrequest_title').addClass('autogenerated-title');
268
269 }
270
271 var useGeneratedDescription = (
272 !codeMirrorInstance._userDefinedValue ||
273 codeMirrorInstance.getValue() === "");
274
275 if (proposedDescription && useGeneratedDescription) {
276 // set proposed content, if we haven't defined our own,
277 // or we don't have description written
278 codeMirrorInstance._userDefinedValue = false; // reset state
279 codeMirrorInstance.setValue(proposedDescription);
280 }
281
282 // refresh our codeMirror so events kicks in and it's change aware
283 codeMirrorInstance.refresh();
284
285 var url_data = {
286 'repo_name': targetRepo(),
287 'target_repo': sourceRepo(),
288 'source_ref': targetRef()[2],
289 'source_ref_type': 'rev',
290 'target_ref': sourceRef()[2],
291 'target_ref_type': 'rev',
292 'merge': true,
293 '_': Date.now() // bypass browser caching
294 }; // gather the source/target ref and repo here
295 var url = pyroutes.url('repo_compare', url_data);
296
297 var msg = '<input id="common_ancestor" type="hidden" name="common_ancestor" value="{0}">'.format(commonAncestorId);
298 msg += '<input type="hidden" name="__start__" value="revisions:sequence">'
299
300 $.each(commitElements, function(idx, value) {
301 msg += '<input type="hidden" name="revisions" value="{0}">'.format(value["raw_id"]);
302 });
303
304 msg += '<input type="hidden" name="__end__" value="revisions:sequence">'
305 msg += _ngettext(
306 'This pull requests will consist of <strong>{0} commit</strong>.',
307 'This pull requests will consist of <strong>{0} commits</strong>.',
308 commitElements.length).format(commitElements.length)
309
310 msg += '\n';
311 msg += _ngettext(
312 '<strong>{0} file</strong> changed, ',
313 '<strong>{0} files</strong> changed, ',
314 files.length).format(files.length)
315 msg += '<span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted</span>.'.format(added, deleted)
316
317 msg += '\n\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
318
319 if (commitElements.length) {
320 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
321 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
322 }
323 else {
324 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
325 }
326
327 };
328
244 reviewersController = new ReviewersController();
329 reviewersController = new ReviewersController();
330 reviewersController.diffDataHandler = diffDataHandler;
245
331
246 var queryTargetRepo = function(self, query) {
332 var queryTargetRepo = function(self, query) {
247 // cache ALL results if query is empty
333 // cache ALL results if query is empty
@@ -260,9 +346,10 b''
260 self.cachedDataSource[cacheKey] = data;
346 self.cachedDataSource[cacheKey] = data;
261 query.callback({results: data.results});
347 query.callback({results: data.results});
262 },
348 },
263 error: function(data, textStatus, errorThrown) {
349 error: function(jqXHR, textStatus, errorThrown) {
264 alert(
350 var prefix = "Error while fetching entries.\n"
265 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
351 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
352 ajaxErrorSwal(message);
266 }
353 }
267 });
354 });
268 }
355 }
@@ -285,99 +372,6 b''
285 query.callback({results: data.results});
372 query.callback({results: data.results});
286 };
373 };
287
374
288 var loadRepoRefDiffPreview = function() {
289
290 var url_data = {
291 'repo_name': targetRepo(),
292 'target_repo': sourceRepo(),
293 'source_ref': targetRef()[2],
294 'source_ref_type': 'rev',
295 'target_ref': sourceRef()[2],
296 'target_ref_type': 'rev',
297 'merge': true,
298 '_': Date.now() // bypass browser caching
299 }; // gather the source/target ref and repo here
300
301 if (sourceRef().length !== 3 || targetRef().length !== 3) {
302 prButtonLock(true, "${_('Please select source and target')}");
303 return;
304 }
305 var url = pyroutes.url('repo_compare', url_data);
306
307 // lock PR button, so we cannot send PR before it's calculated
308 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
309
310 if (loadRepoRefDiffPreview._currentRequest) {
311 loadRepoRefDiffPreview._currentRequest.abort();
312 }
313
314 loadRepoRefDiffPreview._currentRequest = $.get(url)
315 .error(function(data, textStatus, errorThrown) {
316 if (textStatus !== 'abort') {
317 alert(
318 "Error while processing request.\nError code {0} ({1}).".format(
319 data.status, data.statusText));
320 }
321
322 })
323 .done(function(data) {
324 loadRepoRefDiffPreview._currentRequest = null;
325 $('#pull_request_overview').html(data);
326
327 var commitElements = $(data).find('tr[commit_id]');
328
329 var prTitleAndDesc = getTitleAndDescription(
330 sourceRef()[1], commitElements, 5);
331
332 var title = prTitleAndDesc[0];
333 var proposedDescription = prTitleAndDesc[1];
334
335 var useGeneratedTitle = (
336 $('#pullrequest_title').hasClass('autogenerated-title') ||
337 $('#pullrequest_title').val() === "");
338
339 if (title && useGeneratedTitle) {
340 // use generated title if we haven't specified our own
341 $('#pullrequest_title').val(title);
342 $('#pullrequest_title').addClass('autogenerated-title');
343
344 }
345
346 var useGeneratedDescription = (
347 !codeMirrorInstance._userDefinedValue ||
348 codeMirrorInstance.getValue() === "");
349
350 if (proposedDescription && useGeneratedDescription) {
351 // set proposed content, if we haven't defined our own,
352 // or we don't have description written
353 codeMirrorInstance._userDefinedValue = false; // reset state
354 codeMirrorInstance.setValue(proposedDescription);
355 }
356
357 // refresh our codeMirror so events kicks in and it's change aware
358 codeMirrorInstance.refresh();
359
360 var msg = '';
361 if (commitElements.length === 1) {
362 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
363 } else {
364 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
365 }
366
367 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
368
369 if (commitElements.length) {
370 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
371 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
372 }
373 else {
374 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
375 }
376
377
378 });
379 };
380
381 var Select2Box = function(element, overrides) {
375 var Select2Box = function(element, overrides) {
382 var globalDefaults = {
376 var globalDefaults = {
383 dropdownAutoWidth: true,
377 dropdownAutoWidth: true,
@@ -475,13 +469,11 b''
475 targetRepoSelect2.initRepo(defaultTargetRepo, false);
469 targetRepoSelect2.initRepo(defaultTargetRepo, false);
476
470
477 $sourceRef.on('change', function(e){
471 $sourceRef.on('change', function(e){
478 loadRepoRefDiffPreview();
479 reviewersController.loadDefaultReviewers(
472 reviewersController.loadDefaultReviewers(
480 sourceRepo(), sourceRef(), targetRepo(), targetRef());
473 sourceRepo(), sourceRef(), targetRepo(), targetRef());
481 });
474 });
482
475
483 $targetRef.on('change', function(e){
476 $targetRef.on('change', function(e){
484 loadRepoRefDiffPreview();
485 reviewersController.loadDefaultReviewers(
477 reviewersController.loadDefaultReviewers(
486 sourceRepo(), sourceRef(), targetRepo(), targetRef());
478 sourceRepo(), sourceRef(), targetRepo(), targetRef());
487 });
479 });
@@ -501,10 +493,11 b''
501 success: function(data) {
493 success: function(data) {
502 $('#target_ref_loading').hide();
494 $('#target_ref_loading').hide();
503 targetRepoChanged(data);
495 targetRepoChanged(data);
504 loadRepoRefDiffPreview();
505 },
496 },
506 error: function(data, textStatus, errorThrown) {
497 error: function(jqXHR, textStatus, errorThrown) {
507 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
498 var prefix = "Error while fetching entries.\n"
499 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
500 ajaxErrorSwal(message);
508 }
501 }
509 })
502 })
510
503
@@ -530,8 +523,8 b''
530 % if c.default_source_ref:
523 % if c.default_source_ref:
531 // in case we have a pre-selected value, use it now
524 // in case we have a pre-selected value, use it now
532 $sourceRef.select2('val', '${c.default_source_ref}');
525 $sourceRef.select2('val', '${c.default_source_ref}');
533 // diff preview load
526
534 loadRepoRefDiffPreview();
527
535 // default reviewers
528 // default reviewers
536 reviewersController.loadDefaultReviewers(
529 reviewersController.loadDefaultReviewers(
537 sourceRepo(), sourceRef(), targetRepo(), targetRef());
530 sourceRepo(), sourceRef(), targetRepo(), targetRef());
@@ -11,15 +11,6 b''
11
11
12 <%def name="breadcrumbs_links()">
12 <%def name="breadcrumbs_links()">
13
13
14 <div id="pr-title">
15 % if c.pull_request.is_closed():
16 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
17 % endif
18 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
19 </div>
20 <div id="pr-title-edit" class="input" style="display: none;">
21 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
22 </div>
23 </%def>
14 </%def>
24
15
25 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
@@ -40,11 +31,19 b''
40
31
41 <div class="box">
32 <div class="box">
42
33
43 ${self.breadcrumbs()}
44
45 <div class="box pr-summary">
34 <div class="box pr-summary">
46
35
47 <div class="summary-details block-left">
36 <div class="summary-details block-left">
37 <div id="pr-title">
38 % if c.pull_request.is_closed():
39 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
40 % endif
41 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
42 </div>
43 <div id="pr-title-edit" class="input" style="display: none;">
44 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
45 </div>
46
48 <% summary = lambda n:{False:'summary-short'}.get(n) %>
47 <% summary = lambda n:{False:'summary-short'}.get(n) %>
49 <div class="pr-details-title">
48 <div class="pr-details-title">
50 <div class="pull-left">
49 <div class="pull-left">
@@ -64,8 +63,9 b''
64 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
63 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
65 % if c.allowed_to_delete:
64 % if c.allowed_to_delete:
66 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
65 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
67 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete pull request'),
66 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
68 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
67 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
68 type="submit" value="${_('Delete pull request')}">
69 ${h.end_form()}
69 ${h.end_form()}
70 % else:
70 % else:
71 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
71 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
@@ -295,7 +295,7 b''
295 </div>
295 </div>
296
296
297 ## REVIEWERS
297 ## REVIEWERS
298 <div class="reviewers-title block-right">
298 <div class="reviewers-title first-panel block-right">
299 <div class="pr-details-title">
299 <div class="pr-details-title">
300 ${_('Pull request reviewers')}
300 ${_('Pull request reviewers')}
301 %if c.allowed_to_update:
301 %if c.allowed_to_update:
@@ -471,6 +471,16 b''
471 </div>
471 </div>
472 </div>
472 </div>
473 </div>
473 </div>
474 % elif c.pr_merge_source_commit.changed:
475 <div class="box">
476 <div class="alert alert-info">
477 <div>
478 % if c.pr_merge_source_commit.changed:
479 <strong>${_('There are new changes for {}:{} in source repository, please consider updating this pull request.').format(c.pr_merge_source_commit.ref_spec.type, c.pr_merge_source_commit.ref_spec.name)}</strong>
480 % endif
481 </div>
482 </div>
483 </div>
474 % endif
484 % endif
475
485
476 <div class="compare_view_commits_title">
486 <div class="compare_view_commits_title">
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -40,7 +40,7 b' from rhodecode.events import ('
40 PullRequestUpdateEvent,
40 PullRequestUpdateEvent,
41 PullRequestReviewEvent,
41 PullRequestReviewEvent,
42 PullRequestMergeEvent,
42 PullRequestMergeEvent,
43 PullRequestCloseEvent,
43 PullRequestCloseEvent
44 ])
44 ])
45 def test_pullrequest_events_serialized(EventClass, pr_util, config_stub):
45 def test_pullrequest_events_serialized(EventClass, pr_util, config_stub):
46 pr = pr_util.create_pull_request()
46 pr = pr_util.create_pull_request()
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,6 +20,7 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.utils2 import StrictAttributeDict
23 from rhodecode.tests.events.conftest import EventCatcher
24 from rhodecode.tests.events.conftest import EventCatcher
24
25
25 from rhodecode.lib import hooks_base, utils2
26 from rhodecode.lib import hooks_base, utils2
@@ -28,7 +29,7 b' from rhodecode.events.repo import ('
28 RepoPrePullEvent, RepoPullEvent,
29 RepoPrePullEvent, RepoPullEvent,
29 RepoPrePushEvent, RepoPushEvent,
30 RepoPrePushEvent, RepoPushEvent,
30 RepoPreCreateEvent, RepoCreateEvent,
31 RepoPreCreateEvent, RepoCreateEvent,
31 RepoPreDeleteEvent, RepoDeleteEvent,
32 RepoPreDeleteEvent, RepoDeleteEvent, RepoCommitCommentEvent,
32 )
33 )
33
34
34
35
@@ -121,3 +122,24 b' def test_push_fires_events(scm_extras):'
121 hooks_base.post_pull(scm_extras)
122 hooks_base.post_pull(scm_extras)
122 assert event_catcher.events_types == [RepoPullEvent]
123 assert event_catcher.events_types == [RepoPullEvent]
123
124
125
126 @pytest.mark.parametrize('EventClass', [RepoCommitCommentEvent])
127 def test_repo_commit_event(config_stub, repo_stub, EventClass):
128
129 commit = StrictAttributeDict({
130 'raw_id': 'raw_id',
131 'message': 'message',
132 'branch': 'branch',
133 })
134
135 comment = StrictAttributeDict({
136 'comment_id': 'comment_id',
137 'text': 'text',
138 'comment_type': 'comment_type',
139 'f_path': 'f_path',
140 'line_no': 'line_no',
141 })
142 event = EventClass(repo=repo_stub, commit=commit, comment=comment)
143 data = event.as_dict()
144 assert data['commit']['commit_id']
145 assert data['comment']['comment_id']
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r625:ead8f28a4bc2", "type": "sha", "id": "ead8f28a4bc2f45ecfb148a6b8a89758b9654a84"}, {"text": "r535:c093f94d6d35", "type": "sha", "id": "c093f94d6d358f13c55a687da66c30c41cca4153"}, {"text": "r534:559f640ec08b", "type": "sha", "id": "559f640ec08b2a14c4a9ac863d8ca273545b8885"}, {"text": "r490:02a940b4ee37", "type": "sha", "id": "02a940b4ee371ec64ef5b4c4870a5c89dc7fb98a"}, {"text": "r464:b45a4153a2d7", "type": "sha", "id": "b45a4153a2d7adb8a78b63d35d39fac44a4320a6"}, {"text": "r460:0a54e66b9450", "type": "sha", "id": "0a54e66b94502409074b163cd93c1233dcc0413f"}, {"text": "r457:a7bf2f6bf3d5", "type": "sha", "id": "a7bf2f6bf3d5273da4bcd2032a891acae5a45e2b"}, {"text": "r456:7266de0154b4", "type": "sha", "id": "7266de0154b4da7c42ba3d788876056dbf116b5a"}, {"text": "r455:666de4ee6507", "type": "sha", "id": "666de4ee65074cd3e37ea01e75f65bd3e4c336bb"}, {"text": "r453:91acc599141c", "type": "sha", "id": "91acc599141c87f03e0e3551dcaacf4492632e58"}, {"text": "r442:40a2d5d71b75", "type": "sha", "id": "40a2d5d71b758e7eafc84a324ed55142cba22f42"}, {"text": "r440:d1f898326327", "type": "sha", "id": "d1f898326327e20524fe22417c22d71064fe54a1"}, {"text": "r420:162a36830c23", "type": "sha", "id": "162a36830c23ccf1bf1873157fd0c8d0dfc7c817"}, {"text": "r345:c994f0de03b2", "type": "sha", "id": "c994f0de03b2a0aa848a04fc2c0d7e737dba31fc"}, {"text": "r340:5d3d4d2c262e", "type": "sha", "id": "5d3d4d2c262e17b247d405feceeb09ff7408c940"}, {"text": "r334:4d4278a6390e", "type": "sha", "id": "4d4278a6390e42f4fc777ecf1b9b628e77da8e22"}, {"text": "r298:00dffb625166", "type": "sha", "id": "00dffb62516650bc5050d818eb47ea1ca207954d"}, {"text": "r297:47b6be9a812e", "type": "sha", "id": "47b6be9a812ec3ed0384001a458a759f0f583fe2"}, {"text": "r289:1589fed841cd", "type": "sha", "id": "1589fed841cd9ef33155f8560727809ac3ada2c8"}, {"text": "r285:afafd0ee2821", "type": "sha", "id": "afafd0ee28218ab979678213cb96e9e4dbd7359b"}, {"text": "r284:639b115ed2b0", "type": "sha", "id": "639b115ed2b02017824005b5ae66282c6e25eba8"}, {"text": "r283:fcf7562d7305", "type": "sha", "id": "fcf7562d7305affc94fe20dc89a34aefd2b8aa1e"}, {"text": "r256:ec8cbdb5f364", "type": "sha", "id": "ec8cbdb5f364fce7843cbf148c3d95d86f935339"}, {"text": "r255:0d74d2e2bdf3", "type": "sha", "id": "0d74d2e2bdf3dcd5ee9fe4fcfe9016c5c6486f35"}, {"text": "r243:6894ad7d8223", "type": "sha", "id": "6894ad7d8223b1e6853e9fdaa2c38d3f0cef1e38"}, {"text": "r231:31b3f4b599fa", "type": "sha", "id": "31b3f4b599fae5f12cf438c73403679cdf923d75"}, {"text": "r220:3d2515dd21fb", "type": "sha", "id": "3d2515dd21fb34fe6c5d0029075a863f3e92f5f6"}, {"text": "r186:f804e27aa496", "type": "sha", "id": "f804e27aa4961f2e327f2a10ee373235df20ee21"}, {"text": "r182:7f00513785a1", "type": "sha", "id": "7f00513785a13f273a4387ef086bb795b37f013c"}, {"text": "r181:6efcdc61028c", "type": "sha", "id": "6efcdc61028c8edd1c787b3439fae71b77a17357"}, {"text": "r175:6c0ce52b229a", "type": "sha", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "r165:09788a0b8a54", "type": "sha", "id": "09788a0b8a5455e9678c3959214246574e546d4f"}, {"text": "r163:0164ee729def", "type": "sha", "id": "0164ee729def0a253d6dcb594b5ee2a52fef4748"}, {"text": "r140:33fa32233551", "type": "sha", "id": "33fa3223355104431402a888fa77a4e9956feb3e"}, {"text": "r126:fa014c12c26d", "type": "sha", "id": "fa014c12c26d10ba682fadb78f2a11c24c8118e1"}, {"text": "r111:e686b958768e", "type": "sha", "id": "e686b958768ee96af8029fe19c6050b1a8dd3b2b"}, {"text": "r109:ab5721ca0a08", "type": "sha", "id": "ab5721ca0a081f26bf43d9051e615af2cc99952f"}, {"text": "r108:c877b68d18e7", "type": "sha", "id": "c877b68d18e792a66b7f4c529ea02c8f80801542"}, {"text": "r107:4313566d2e41", "type": "sha", "id": "4313566d2e417cb382948f8d9d7c765330356054"}, {"text": "r104:6c2303a79367", "type": "sha", "id": "6c2303a793671e807d1cfc70134c9ca0767d98c2"}, {"text": "r102:54386793436c", "type": "sha", "id": "54386793436c938cff89326944d4c2702340037d"}, {"text": "r101:54000345d2e7", "type": "sha", "id": "54000345d2e78b03a99d561399e8e548de3f3203"}, {"text": "r99:1c6b3677b37e", "type": "sha", "id": "1c6b3677b37ea064cb4b51714d8f7498f93f4b2b"}, {"text": "r93:2d03ca750a44", "type": "sha", "id": "2d03ca750a44440fb5ea8b751176d1f36f8e8f46"}, {"text": "r92:2a08b128c206", "type": "sha", "id": "2a08b128c206db48c2f0b8f70df060e6db0ae4f8"}, {"text": "r91:30c26513ff1e", "type": "sha", "id": "30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b"}, {"text": "r82:ac71e9503c2c", "type": "sha", "id": "ac71e9503c2ca95542839af0ce7b64011b72ea7c"}, {"text": "r81:12669288fd13", "type": "sha", "id": "12669288fd13adba2a9b7dd5b870cc23ffab92d2"}, {"text": "r76:5a0c84f3e6fe", "type": "sha", "id": "5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382"}, {"text": "r73:12f2f5e2b38e", "type": "sha", "id": "12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5"}, {"text": "r61:5eab1222a7cd", "type": "sha", "id": "5eab1222a7cd4bfcbabc218ca6d04276d4e27378"}, {"text": "r60:f50f42baeed5", "type": "sha", "id": "f50f42baeed5af6518ef4b0cb2f1423f3851a941"}, {"text": "r59:d7e390a45f6a", "type": "sha", "id": "d7e390a45f6aa96f04f5e7f583ad4f867431aa25"}, {"text": "r58:f15c21f97864", "type": "sha", "id": "f15c21f97864b4f071cddfbf2750ec2e23859414"}, {"text": "r57:e906ef056cf5", "type": "sha", "id": "e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade"}, {"text": "r56:ea2b108b48aa", "type": "sha", "id": "ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b"}, {"text": "r50:84dec09632a4", "type": "sha", "id": "84dec09632a4458f79f50ddbbd155506c460b4f9"}, {"text": "r48:0115510b70c7", "type": "sha", "id": "0115510b70c7229dbc5dc49036b32e7d91d23acd"}, {"text": "r46:2a13f185e452", "type": "sha", "id": "2a13f185e4525f9d4b59882791a2d397b90d5ddc"}, {"text": "r30:3bf1c5868e57", "type": "sha", "id": "3bf1c5868e570e39569d094f922d33ced2fa3b2b"}, {"text": "r26:b8d040125747", "type": "sha", "id": "b8d04012574729d2c29886e53b1a43ef16dd00a1"}, {"text": "r24:6970b057cffe", "type": "sha", "id": "6970b057cffe4aab0a792aa634c89f4bebf01441"}, {"text": "r8:dd80b0f6cf50", "type": "sha", "id": "dd80b0f6cf5052f17cc738c2951c4f2070200d7f"}, {"text": "r7:ff7ca51e58c5", "type": "sha", "id": "ff7ca51e58c505fec0dd2491de52c622bb7a806b"}]}, {"text": "Branches", "children": [{"text": "master", "type": "branch", "id": "fd627b9e0dd80b47be81af07c4a98518244ed2f7"}]}, {"text": "Tags", "children": [{"text": "v0.2.2", "type": "tag", "id": "137fea89f304a42321d40488091ee2ed419a3686"}, {"text": "v0.2.1", "type": "tag", "id": "5051d0fa344d4408a2659d9a0348eb2d41868ecf"}, {"text": "v0.2.0", "type": "tag", "id": "599ba911aa24d2981225f3966eb659dfae9e9f30"}, {"text": "v0.1.9", "type": "tag", "id": "341d28f0eec5ddf0b6b77871e13c2bbd6bec685c"}, {"text": "v0.1.8", "type": "tag", "id": "74ebce002c088b8a5ecf40073db09375515ecd68"}, {"text": "v0.1.7", "type": "tag", "id": "4d78bf73b5c22c82b68f902f138f7881b4fffa2c"}, {"text": "v0.1.6", "type": "tag", "id": "0205cb3f44223fb3099d12a77a69c81b798772d9"}, {"text": "v0.1.5", "type": "tag", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "v0.1.4", "type": "tag", "id": "7d735150934cd7645ac3051903add952390324a5"}, {"text": "v0.1.3", "type": "tag", "id": "5a3a8fb005554692b16e21dee62bf02667d8dc3e"}, {"text": "v0.1.2", "type": "tag", "id": "0ba5f8a4660034ff25c0cac2a5baabf5d2791d63"}, {"text": "v0.1.11", "type": "tag", "id": "c60f01b77c42dce653d6b1d3b04689862c261929"}, {"text": "v0.1.10", "type": "tag", "id": "10cddef6b794696066fb346434014f0a56810218"}, {"text": "v0.1.1", "type": "tag", "id": "e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0"}]}], "more": false}
1 {"results": [{"text": "Changesets", "children": [{"text": "r625:ead8f28a4bc2", "at_rev": "", "type": "sha", "id": "ead8f28a4bc2f45ecfb148a6b8a89758b9654a84"}, {"text": "r535:c093f94d6d35", "at_rev": "", "type": "sha", "id": "c093f94d6d358f13c55a687da66c30c41cca4153"}, {"text": "r534:559f640ec08b", "at_rev": "", "type": "sha", "id": "559f640ec08b2a14c4a9ac863d8ca273545b8885"}, {"text": "r490:02a940b4ee37", "at_rev": "", "type": "sha", "id": "02a940b4ee371ec64ef5b4c4870a5c89dc7fb98a"}, {"text": "r464:b45a4153a2d7", "at_rev": "", "type": "sha", "id": "b45a4153a2d7adb8a78b63d35d39fac44a4320a6"}, {"text": "r460:0a54e66b9450", "at_rev": "", "type": "sha", "id": "0a54e66b94502409074b163cd93c1233dcc0413f"}, {"text": "r457:a7bf2f6bf3d5", "at_rev": "", "type": "sha", "id": "a7bf2f6bf3d5273da4bcd2032a891acae5a45e2b"}, {"text": "r456:7266de0154b4", "at_rev": "", "type": "sha", "id": "7266de0154b4da7c42ba3d788876056dbf116b5a"}, {"text": "r455:666de4ee6507", "at_rev": "", "type": "sha", "id": "666de4ee65074cd3e37ea01e75f65bd3e4c336bb"}, {"text": "r453:91acc599141c", "at_rev": "", "type": "sha", "id": "91acc599141c87f03e0e3551dcaacf4492632e58"}, {"text": "r442:40a2d5d71b75", "at_rev": "", "type": "sha", "id": "40a2d5d71b758e7eafc84a324ed55142cba22f42"}, {"text": "r440:d1f898326327", "at_rev": "", "type": "sha", "id": "d1f898326327e20524fe22417c22d71064fe54a1"}, {"text": "r420:162a36830c23", "at_rev": "", "type": "sha", "id": "162a36830c23ccf1bf1873157fd0c8d0dfc7c817"}, {"text": "r345:c994f0de03b2", "at_rev": "", "type": "sha", "id": "c994f0de03b2a0aa848a04fc2c0d7e737dba31fc"}, {"text": "r340:5d3d4d2c262e", "at_rev": "", "type": "sha", "id": "5d3d4d2c262e17b247d405feceeb09ff7408c940"}, {"text": "r334:4d4278a6390e", "at_rev": "", "type": "sha", "id": "4d4278a6390e42f4fc777ecf1b9b628e77da8e22"}, {"text": "r298:00dffb625166", "at_rev": "", "type": "sha", "id": "00dffb62516650bc5050d818eb47ea1ca207954d"}, {"text": "r297:47b6be9a812e", "at_rev": "", "type": "sha", "id": "47b6be9a812ec3ed0384001a458a759f0f583fe2"}, {"text": "r289:1589fed841cd", "at_rev": "", "type": "sha", "id": "1589fed841cd9ef33155f8560727809ac3ada2c8"}, {"text": "r285:afafd0ee2821", "at_rev": "", "type": "sha", "id": "afafd0ee28218ab979678213cb96e9e4dbd7359b"}, {"text": "r284:639b115ed2b0", "at_rev": "", "type": "sha", "id": "639b115ed2b02017824005b5ae66282c6e25eba8"}, {"text": "r283:fcf7562d7305", "at_rev": "", "type": "sha", "id": "fcf7562d7305affc94fe20dc89a34aefd2b8aa1e"}, {"text": "r256:ec8cbdb5f364", "at_rev": "", "type": "sha", "id": "ec8cbdb5f364fce7843cbf148c3d95d86f935339"}, {"text": "r255:0d74d2e2bdf3", "at_rev": "", "type": "sha", "id": "0d74d2e2bdf3dcd5ee9fe4fcfe9016c5c6486f35"}, {"text": "r243:6894ad7d8223", "at_rev": "", "type": "sha", "id": "6894ad7d8223b1e6853e9fdaa2c38d3f0cef1e38"}, {"text": "r231:31b3f4b599fa", "at_rev": "", "type": "sha", "id": "31b3f4b599fae5f12cf438c73403679cdf923d75"}, {"text": "r220:3d2515dd21fb", "at_rev": "", "type": "sha", "id": "3d2515dd21fb34fe6c5d0029075a863f3e92f5f6"}, {"text": "r186:f804e27aa496", "at_rev": "", "type": "sha", "id": "f804e27aa4961f2e327f2a10ee373235df20ee21"}, {"text": "r182:7f00513785a1", "at_rev": "", "type": "sha", "id": "7f00513785a13f273a4387ef086bb795b37f013c"}, {"text": "r181:6efcdc61028c", "at_rev": "", "type": "sha", "id": "6efcdc61028c8edd1c787b3439fae71b77a17357"}, {"text": "r175:6c0ce52b229a", "at_rev": "", "type": "sha", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "r165:09788a0b8a54", "at_rev": "", "type": "sha", "id": "09788a0b8a5455e9678c3959214246574e546d4f"}, {"text": "r163:0164ee729def", "at_rev": "", "type": "sha", "id": "0164ee729def0a253d6dcb594b5ee2a52fef4748"}, {"text": "r140:33fa32233551", "at_rev": "", "type": "sha", "id": "33fa3223355104431402a888fa77a4e9956feb3e"}, {"text": "r126:fa014c12c26d", "at_rev": "", "type": "sha", "id": "fa014c12c26d10ba682fadb78f2a11c24c8118e1"}, {"text": "r111:e686b958768e", "at_rev": "", "type": "sha", "id": "e686b958768ee96af8029fe19c6050b1a8dd3b2b"}, {"text": "r109:ab5721ca0a08", "at_rev": "", "type": "sha", "id": "ab5721ca0a081f26bf43d9051e615af2cc99952f"}, {"text": "r108:c877b68d18e7", "at_rev": "", "type": "sha", "id": "c877b68d18e792a66b7f4c529ea02c8f80801542"}, {"text": "r107:4313566d2e41", "at_rev": "", "type": "sha", "id": "4313566d2e417cb382948f8d9d7c765330356054"}, {"text": "r104:6c2303a79367", "at_rev": "", "type": "sha", "id": "6c2303a793671e807d1cfc70134c9ca0767d98c2"}, {"text": "r102:54386793436c", "at_rev": "", "type": "sha", "id": "54386793436c938cff89326944d4c2702340037d"}, {"text": "r101:54000345d2e7", "at_rev": "", "type": "sha", "id": "54000345d2e78b03a99d561399e8e548de3f3203"}, {"text": "r99:1c6b3677b37e", "at_rev": "", "type": "sha", "id": "1c6b3677b37ea064cb4b51714d8f7498f93f4b2b"}, {"text": "r93:2d03ca750a44", "at_rev": "", "type": "sha", "id": "2d03ca750a44440fb5ea8b751176d1f36f8e8f46"}, {"text": "r92:2a08b128c206", "at_rev": "", "type": "sha", "id": "2a08b128c206db48c2f0b8f70df060e6db0ae4f8"}, {"text": "r91:30c26513ff1e", "at_rev": "", "type": "sha", "id": "30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b"}, {"text": "r82:ac71e9503c2c", "at_rev": "", "type": "sha", "id": "ac71e9503c2ca95542839af0ce7b64011b72ea7c"}, {"text": "r81:12669288fd13", "at_rev": "", "type": "sha", "id": "12669288fd13adba2a9b7dd5b870cc23ffab92d2"}, {"text": "r76:5a0c84f3e6fe", "at_rev": "", "type": "sha", "id": "5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382"}, {"text": "r73:12f2f5e2b38e", "at_rev": "", "type": "sha", "id": "12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5"}, {"text": "r61:5eab1222a7cd", "at_rev": "", "type": "sha", "id": "5eab1222a7cd4bfcbabc218ca6d04276d4e27378"}, {"text": "r60:f50f42baeed5", "at_rev": "", "type": "sha", "id": "f50f42baeed5af6518ef4b0cb2f1423f3851a941"}, {"text": "r59:d7e390a45f6a", "at_rev": "", "type": "sha", "id": "d7e390a45f6aa96f04f5e7f583ad4f867431aa25"}, {"text": "r58:f15c21f97864", "at_rev": "", "type": "sha", "id": "f15c21f97864b4f071cddfbf2750ec2e23859414"}, {"text": "r57:e906ef056cf5", "at_rev": "", "type": "sha", "id": "e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade"}, {"text": "r56:ea2b108b48aa", "at_rev": "", "type": "sha", "id": "ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b"}, {"text": "r50:84dec09632a4", "at_rev": "", "type": "sha", "id": "84dec09632a4458f79f50ddbbd155506c460b4f9"}, {"text": "r48:0115510b70c7", "at_rev": "", "type": "sha", "id": "0115510b70c7229dbc5dc49036b32e7d91d23acd"}, {"text": "r46:2a13f185e452", "at_rev": "", "type": "sha", "id": "2a13f185e4525f9d4b59882791a2d397b90d5ddc"}, {"text": "r30:3bf1c5868e57", "at_rev": "", "type": "sha", "id": "3bf1c5868e570e39569d094f922d33ced2fa3b2b"}, {"text": "r26:b8d040125747", "at_rev": "", "type": "sha", "id": "b8d04012574729d2c29886e53b1a43ef16dd00a1"}, {"text": "r24:6970b057cffe", "at_rev": "", "type": "sha", "id": "6970b057cffe4aab0a792aa634c89f4bebf01441"}, {"text": "r8:dd80b0f6cf50", "at_rev": "", "type": "sha", "id": "dd80b0f6cf5052f17cc738c2951c4f2070200d7f"}, {"text": "r7:ff7ca51e58c5", "at_rev": "", "type": "sha", "id": "ff7ca51e58c505fec0dd2491de52c622bb7a806b"}]}, {"text": "Branches", "children": [{"text": "master", "at_rev": "master", "type": "branch", "id": "fd627b9e0dd80b47be81af07c4a98518244ed2f7"}]}, {"text": "Tags", "children": [{"text": "v0.2.2", "at_rev": "v0.2.2", "type": "tag", "id": "137fea89f304a42321d40488091ee2ed419a3686"}, {"text": "v0.2.1", "at_rev": "v0.2.1", "type": "tag", "id": "5051d0fa344d4408a2659d9a0348eb2d41868ecf"}, {"text": "v0.2.0", "at_rev": "v0.2.0", "type": "tag", "id": "599ba911aa24d2981225f3966eb659dfae9e9f30"}, {"text": "v0.1.9", "at_rev": "v0.1.9", "type": "tag", "id": "341d28f0eec5ddf0b6b77871e13c2bbd6bec685c"}, {"text": "v0.1.8", "at_rev": "v0.1.8", "type": "tag", "id": "74ebce002c088b8a5ecf40073db09375515ecd68"}, {"text": "v0.1.7", "at_rev": "v0.1.7", "type": "tag", "id": "4d78bf73b5c22c82b68f902f138f7881b4fffa2c"}, {"text": "v0.1.6", "at_rev": "v0.1.6", "type": "tag", "id": "0205cb3f44223fb3099d12a77a69c81b798772d9"}, {"text": "v0.1.5", "at_rev": "v0.1.5", "type": "tag", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "v0.1.4", "at_rev": "v0.1.4", "type": "tag", "id": "7d735150934cd7645ac3051903add952390324a5"}, {"text": "v0.1.3", "at_rev": "v0.1.3", "type": "tag", "id": "5a3a8fb005554692b16e21dee62bf02667d8dc3e"}, {"text": "v0.1.2", "at_rev": "v0.1.2", "type": "tag", "id": "0ba5f8a4660034ff25c0cac2a5baabf5d2791d63"}, {"text": "v0.1.11", "at_rev": "v0.1.11", "type": "tag", "id": "c60f01b77c42dce653d6b1d3b04689862c261929"}, {"text": "v0.1.10", "at_rev": "v0.1.10", "type": "tag", "id": "10cddef6b794696066fb346434014f0a56810218"}, {"text": "v0.1.1", "at_rev": "v0.1.1", "type": "tag", "id": "e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0"}]}], "more": false}
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r648:dbec37a0d5ca (default)", "type": "sha", "id": "dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6"}, {"text": "r639:1d20ed9eda94 (default)", "type": "sha", "id": "1d20ed9eda9482d46ff0a6af5812550218b3ff15"}, {"text": "r547:0173395e8227 (default)", "type": "sha", "id": "0173395e822797f098799ed95c1a81b6a547a9ad"}, {"text": "r546:afbb45ade933 (default)", "type": "sha", "id": "afbb45ade933a8182f1d8ec5d4d1bb2de2572043"}, {"text": "r502:6f093e30cac3 (default)", "type": "sha", "id": "6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7"}, {"text": "r476:c7e2212dd2ae (default)", "type": "sha", "id": "c7e2212dd2ae975d1d06534a3d7e317165c06960"}, {"text": "r472:45477506df79 (default)", "type": "sha", "id": "45477506df79f701bf69419aac3e1f0fed3c5bcf"}, {"text": "r469:5fc76cb25d11 (default)", "type": "sha", "id": "5fc76cb25d11e07c60de040f78b8cd265ff10d53"}, {"text": "r468:b073433cf899 (default)", "type": "sha", "id": "b073433cf8994969ee5cd7cce84cbe587bb880b2"}, {"text": "r467:7a74dbfcacd1 (default)", "type": "sha", "id": "7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96"}, {"text": "r465:71ee52cc4d62 (default)", "type": "sha", "id": "71ee52cc4d629096bdbee036325975dac2af4501"}, {"text": "r452:a5b217d26c5f (default)", "type": "sha", "id": "a5b217d26c5f111e72bae4de672b084ee0fbf75c"}, {"text": "r450:47aedd538bf6 (default)", "type": "sha", "id": "47aedd538bf616eedcb0e7d630ea476df0e159c7"}, {"text": "r432:8e4915fa32d7 (default)", "type": "sha", "id": "8e4915fa32d727dcbf09746f637a5f82e539511e"}, {"text": "r356:25213a5fbb04 (default)", "type": "sha", "id": "25213a5fbb048dff8ba65d21e466a835536e5b70"}, {"text": "r351:23debcedddc1 (default)", "type": "sha", "id": "23debcedddc1c23c14be33e713e7786d4a9de471"}, {"text": "r342:61e25b2a90a1 (default)", "type": "sha", "id": "61e25b2a90a19e7fffd75dea1e4c7e20df526bbe"}, {"text": "r318:fb95b340e0d0 (webvcs)", "type": "sha", "id": "fb95b340e0d03fa51f33c56c991c08077c99303e"}, {"text": "r303:bda35e0e564f (default)", "type": "sha", "id": "bda35e0e564fbbc5cd26fe0a37fb647a254c99fe"}, {"text": "r302:97ff74896d7d (default)", "type": "sha", "id": "97ff74896d7dbf3115a337a421d44b55154acc89"}, {"text": "r293:cec3473c3fdb (default)", "type": "sha", "id": "cec3473c3fdb9599c98067182a075b49bde570f9"}, {"text": "r289:0e86c43eef86 (default)", "type": "sha", "id": "0e86c43eef866a013a587666a877c879899599bb"}, {"text": "r288:91a27c312808 (default)", "type": "sha", "id": "91a27c312808100cf20a602f78befbbff9d89bfd"}, {"text": "r287:400e36a1670a (default)", "type": "sha", "id": "400e36a1670a57d11e3edcb5b07bf82c30006d0b"}, {"text": "r261:014fb17dfc95 (default)", "type": "sha", "id": "014fb17dfc95b0995e838c565376bf9a993e230a"}, {"text": "r260:cca7aebbc4d6 (default)", "type": "sha", "id": "cca7aebbc4d6125798446b11e69dc8847834a982"}, {"text": "r258:14cdb2957c01 (workdir)", "type": "sha", "id": "14cdb2957c011a5feba36f50d960d9832ba0f0c1"}, {"text": "r245:34df20118ed7 (default)", "type": "sha", "id": "34df20118ed74b5987d22a579e8a60e903da5bf8"}, {"text": "r233:0375d9042a64 (workdir)", "type": "sha", "id": "0375d9042a64a1ac1641528f0f0668f9a339e86d"}, {"text": "r222:94aa45fc1806 (workdir)", "type": "sha", "id": "94aa45fc1806c04d4ba640933edf682c22478453"}, {"text": "r188:7ed99bc73881 (default)", "type": "sha", "id": "7ed99bc738818879941e3ce20243f8856a7cfc84"}, {"text": "r184:1e85975528bc (default)", "type": "sha", "id": "1e85975528bcebe853732a9e5fb8dbf4461f6bb2"}, {"text": "r183:ed30beddde7b (default)", "type": "sha", "id": "ed30beddde7bbddb26042625be19bcd11576c1dd"}, {"text": "r177:a6664e18181c (default)", "type": "sha", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "r167:8911406ad776 (default)", "type": "sha", "id": "8911406ad776fdd3d0b9932a2e89677e57405a48"}, {"text": "r165:aa957ed78c35 (default)", "type": "sha", "id": "aa957ed78c35a1541f508d2ec90e501b0a9e3167"}, {"text": "r140:48e11b73e94c (default)", "type": "sha", "id": "48e11b73e94c0db33e736eaeea692f990cb0b5f1"}, {"text": "r126:adf3cbf48329 (default)", "type": "sha", "id": "adf3cbf483298563b968a6c673cd5bde5f7d5eea"}, {"text": "r113:6249fd0fb2cf (git)", "type": "sha", "id": "6249fd0fb2cfb1411e764129f598e2cf0de79a6f"}, {"text": "r109:75feb4c33e81 (default)", "type": "sha", "id": "75feb4c33e81186c87eac740cee2447330288412"}, {"text": "r108:9a4dc232ecdc (default)", "type": "sha", "id": "9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d"}, {"text": "r107:595cce4efa21 (default)", "type": "sha", "id": "595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d"}, {"text": "r104:4a8bd421fbc2 (default)", "type": "sha", "id": "4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da"}, {"text": "r102:57be63fc8f85 (default)", "type": "sha", "id": "57be63fc8f85e65a0106a53187f7316f8c487ffa"}, {"text": "r101:5530bd87f7e2 (git)", "type": "sha", "id": "5530bd87f7e2e124a64d07cb2654c997682128be"}, {"text": "r99:e516008b1c93 (default)", "type": "sha", "id": "e516008b1c93f142263dc4b7961787cbad654ce1"}, {"text": "r93:41f43fc74b8b (default)", "type": "sha", "id": "41f43fc74b8b285984554532eb105ac3be5c434f"}, {"text": "r92:cc66b61b8455 (default)", "type": "sha", "id": "cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e"}, {"text": "r91:73ab5b616b32 (default)", "type": "sha", "id": "73ab5b616b3271b0518682fb4988ce421de8099f"}, {"text": "r82:e0da75f308c0 (default)", "type": "sha", "id": "e0da75f308c0f18f98e9ce6257626009fdda2b39"}, {"text": "r81:fb2e41e0f081 (default)", "type": "sha", "id": "fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611"}, {"text": "r76:602ae2f5e7ad (default)", "type": "sha", "id": "602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028"}, {"text": "r73:a066b25d5df7 (default)", "type": "sha", "id": "a066b25d5df7016b45a41b7e2a78c33b57adc235"}, {"text": "r61:637a933c9059 (web)", "type": "sha", "id": "637a933c905958ce5151f154147c25c1c7b68832"}, {"text": "r60:0c21004effeb (web)", "type": "sha", "id": "0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc"}, {"text": "r59:a1f39c56d3f1 (web)", "type": "sha", "id": "a1f39c56d3f1d52d5fb5920370a2a2716cd9a444"}, {"text": "r58:97d32df05c71 (web)", "type": "sha", "id": "97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f"}, {"text": "r57:08eaf1451771 (web)", "type": "sha", "id": "08eaf14517718dccea4b67755a93368341aca919"}, {"text": "r56:22f71ad26526 (web)", "type": "sha", "id": "22f71ad265265a53238359c883aa976e725aa07d"}, {"text": "r49:97501f02b7b4 (web)", "type": "sha", "id": "97501f02b7b4330924b647755663a2d90a5e638d"}, {"text": "r47:86ede6754f2b (web)", "type": "sha", "id": "86ede6754f2b27309452bb11f997386ae01d0e5a"}, {"text": "r45:014c40c0203c (web)", "type": "sha", "id": "014c40c0203c423dc19ecf94644f7cac9d4cdce0"}, {"text": "r30:ee87846a61c1 (default)", "type": "sha", "id": "ee87846a61c12153b51543bf860e1026c6d3dcba"}, {"text": "r26:9bb326a04ae5 (default)", "type": "sha", "id": "9bb326a04ae5d98d437dece54be04f830cf1edd9"}, {"text": "r24:536c1a194283 (default)", "type": "sha", "id": "536c1a19428381cfea92ac44985304f6a8049569"}, {"text": "r8:dc5d2c0661b6 (default)", "type": "sha", "id": "dc5d2c0661b61928834a785d3e64a3f80d3aad9c"}, {"text": "r7:3803844fdbd3 (default)", "type": "sha", "id": "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"}]}, {"text": "Branches", "children": [{"text": "default", "type": "branch", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}, {"text": "stable", "type": "branch", "id": "4f7e2131323e0749a740c0a56ab68ae9269c562a"}]}, {"text": "Tags", "children": [{"text": "v0.2.0", "type": "tag", "id": "2c96c02def9a7c997f33047761a53943e6254396"}, {"text": "v0.1.9", "type": "tag", "id": "8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9"}, {"text": "v0.1.8", "type": "tag", "id": "ecb25ba9c96faf1e65a0bc3fd914918420a2f116"}, {"text": "v0.1.7", "type": "tag", "id": "f67633a2894edaf28513706d558205fa93df9209"}, {"text": "v0.1.6", "type": "tag", "id": "02b38c0eb6f982174750c0e309ff9faddc0c7e12"}, {"text": "v0.1.5", "type": "tag", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "v0.1.4", "type": "tag", "id": "fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200"}, {"text": "v0.1.3", "type": "tag", "id": "17544fbfcd33ffb439e2b728b5d526b1ef30bfcf"}, {"text": "v0.1.2", "type": "tag", "id": "a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720"}, {"text": "v0.1.11", "type": "tag", "id": "fef5bfe1dc17611d5fb59a7f6f95c55c3606f933"}, {"text": "v0.1.10", "type": "tag", "id": "92831aebf2f8dd4879e897024b89d09af214df1c"}, {"text": "v0.1.1", "type": "tag", "id": "eb3a60fc964309c1a318b8dfe26aa2d1586c85ae"}, {"text": "tip", "type": "tag", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}]}], "more": false} No newline at end of file
1 {"results": [{"text": "Changesets", "children": [{"text": "r648:dbec37a0d5ca (default)", "at_rev": "", "type": "sha", "id": "dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6"}, {"text": "r639:1d20ed9eda94 (default)", "at_rev": "", "type": "sha", "id": "1d20ed9eda9482d46ff0a6af5812550218b3ff15"}, {"text": "r547:0173395e8227 (default)", "at_rev": "", "type": "sha", "id": "0173395e822797f098799ed95c1a81b6a547a9ad"}, {"text": "r546:afbb45ade933 (default)", "at_rev": "", "type": "sha", "id": "afbb45ade933a8182f1d8ec5d4d1bb2de2572043"}, {"text": "r502:6f093e30cac3 (default)", "at_rev": "", "type": "sha", "id": "6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7"}, {"text": "r476:c7e2212dd2ae (default)", "at_rev": "", "type": "sha", "id": "c7e2212dd2ae975d1d06534a3d7e317165c06960"}, {"text": "r472:45477506df79 (default)", "at_rev": "", "type": "sha", "id": "45477506df79f701bf69419aac3e1f0fed3c5bcf"}, {"text": "r469:5fc76cb25d11 (default)", "at_rev": "", "type": "sha", "id": "5fc76cb25d11e07c60de040f78b8cd265ff10d53"}, {"text": "r468:b073433cf899 (default)", "at_rev": "", "type": "sha", "id": "b073433cf8994969ee5cd7cce84cbe587bb880b2"}, {"text": "r467:7a74dbfcacd1 (default)", "at_rev": "", "type": "sha", "id": "7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96"}, {"text": "r465:71ee52cc4d62 (default)", "at_rev": "", "type": "sha", "id": "71ee52cc4d629096bdbee036325975dac2af4501"}, {"text": "r452:a5b217d26c5f (default)", "at_rev": "", "type": "sha", "id": "a5b217d26c5f111e72bae4de672b084ee0fbf75c"}, {"text": "r450:47aedd538bf6 (default)", "at_rev": "", "type": "sha", "id": "47aedd538bf616eedcb0e7d630ea476df0e159c7"}, {"text": "r432:8e4915fa32d7 (default)", "at_rev": "", "type": "sha", "id": "8e4915fa32d727dcbf09746f637a5f82e539511e"}, {"text": "r356:25213a5fbb04 (default)", "at_rev": "", "type": "sha", "id": "25213a5fbb048dff8ba65d21e466a835536e5b70"}, {"text": "r351:23debcedddc1 (default)", "at_rev": "", "type": "sha", "id": "23debcedddc1c23c14be33e713e7786d4a9de471"}, {"text": "r342:61e25b2a90a1 (default)", "at_rev": "", "type": "sha", "id": "61e25b2a90a19e7fffd75dea1e4c7e20df526bbe"}, {"text": "r318:fb95b340e0d0 (webvcs)", "at_rev": "", "type": "sha", "id": "fb95b340e0d03fa51f33c56c991c08077c99303e"}, {"text": "r303:bda35e0e564f (default)", "at_rev": "", "type": "sha", "id": "bda35e0e564fbbc5cd26fe0a37fb647a254c99fe"}, {"text": "r302:97ff74896d7d (default)", "at_rev": "", "type": "sha", "id": "97ff74896d7dbf3115a337a421d44b55154acc89"}, {"text": "r293:cec3473c3fdb (default)", "at_rev": "", "type": "sha", "id": "cec3473c3fdb9599c98067182a075b49bde570f9"}, {"text": "r289:0e86c43eef86 (default)", "at_rev": "", "type": "sha", "id": "0e86c43eef866a013a587666a877c879899599bb"}, {"text": "r288:91a27c312808 (default)", "at_rev": "", "type": "sha", "id": "91a27c312808100cf20a602f78befbbff9d89bfd"}, {"text": "r287:400e36a1670a (default)", "at_rev": "", "type": "sha", "id": "400e36a1670a57d11e3edcb5b07bf82c30006d0b"}, {"text": "r261:014fb17dfc95 (default)", "at_rev": "", "type": "sha", "id": "014fb17dfc95b0995e838c565376bf9a993e230a"}, {"text": "r260:cca7aebbc4d6 (default)", "at_rev": "", "type": "sha", "id": "cca7aebbc4d6125798446b11e69dc8847834a982"}, {"text": "r258:14cdb2957c01 (workdir)", "at_rev": "", "type": "sha", "id": "14cdb2957c011a5feba36f50d960d9832ba0f0c1"}, {"text": "r245:34df20118ed7 (default)", "at_rev": "", "type": "sha", "id": "34df20118ed74b5987d22a579e8a60e903da5bf8"}, {"text": "r233:0375d9042a64 (workdir)", "at_rev": "", "type": "sha", "id": "0375d9042a64a1ac1641528f0f0668f9a339e86d"}, {"text": "r222:94aa45fc1806 (workdir)", "at_rev": "", "type": "sha", "id": "94aa45fc1806c04d4ba640933edf682c22478453"}, {"text": "r188:7ed99bc73881 (default)", "at_rev": "", "type": "sha", "id": "7ed99bc738818879941e3ce20243f8856a7cfc84"}, {"text": "r184:1e85975528bc (default)", "at_rev": "", "type": "sha", "id": "1e85975528bcebe853732a9e5fb8dbf4461f6bb2"}, {"text": "r183:ed30beddde7b (default)", "at_rev": "", "type": "sha", "id": "ed30beddde7bbddb26042625be19bcd11576c1dd"}, {"text": "r177:a6664e18181c (default)", "at_rev": "", "type": "sha", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "r167:8911406ad776 (default)", "at_rev": "", "type": "sha", "id": "8911406ad776fdd3d0b9932a2e89677e57405a48"}, {"text": "r165:aa957ed78c35 (default)", "at_rev": "", "type": "sha", "id": "aa957ed78c35a1541f508d2ec90e501b0a9e3167"}, {"text": "r140:48e11b73e94c (default)", "at_rev": "", "type": "sha", "id": "48e11b73e94c0db33e736eaeea692f990cb0b5f1"}, {"text": "r126:adf3cbf48329 (default)", "at_rev": "", "type": "sha", "id": "adf3cbf483298563b968a6c673cd5bde5f7d5eea"}, {"text": "r113:6249fd0fb2cf (git)", "at_rev": "", "type": "sha", "id": "6249fd0fb2cfb1411e764129f598e2cf0de79a6f"}, {"text": "r109:75feb4c33e81 (default)", "at_rev": "", "type": "sha", "id": "75feb4c33e81186c87eac740cee2447330288412"}, {"text": "r108:9a4dc232ecdc (default)", "at_rev": "", "type": "sha", "id": "9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d"}, {"text": "r107:595cce4efa21 (default)", "at_rev": "", "type": "sha", "id": "595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d"}, {"text": "r104:4a8bd421fbc2 (default)", "at_rev": "", "type": "sha", "id": "4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da"}, {"text": "r102:57be63fc8f85 (default)", "at_rev": "", "type": "sha", "id": "57be63fc8f85e65a0106a53187f7316f8c487ffa"}, {"text": "r101:5530bd87f7e2 (git)", "at_rev": "", "type": "sha", "id": "5530bd87f7e2e124a64d07cb2654c997682128be"}, {"text": "r99:e516008b1c93 (default)", "at_rev": "", "type": "sha", "id": "e516008b1c93f142263dc4b7961787cbad654ce1"}, {"text": "r93:41f43fc74b8b (default)", "at_rev": "", "type": "sha", "id": "41f43fc74b8b285984554532eb105ac3be5c434f"}, {"text": "r92:cc66b61b8455 (default)", "at_rev": "", "type": "sha", "id": "cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e"}, {"text": "r91:73ab5b616b32 (default)", "at_rev": "", "type": "sha", "id": "73ab5b616b3271b0518682fb4988ce421de8099f"}, {"text": "r82:e0da75f308c0 (default)", "at_rev": "", "type": "sha", "id": "e0da75f308c0f18f98e9ce6257626009fdda2b39"}, {"text": "r81:fb2e41e0f081 (default)", "at_rev": "", "type": "sha", "id": "fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611"}, {"text": "r76:602ae2f5e7ad (default)", "at_rev": "", "type": "sha", "id": "602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028"}, {"text": "r73:a066b25d5df7 (default)", "at_rev": "", "type": "sha", "id": "a066b25d5df7016b45a41b7e2a78c33b57adc235"}, {"text": "r61:637a933c9059 (web)", "at_rev": "", "type": "sha", "id": "637a933c905958ce5151f154147c25c1c7b68832"}, {"text": "r60:0c21004effeb (web)", "at_rev": "", "type": "sha", "id": "0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc"}, {"text": "r59:a1f39c56d3f1 (web)", "at_rev": "", "type": "sha", "id": "a1f39c56d3f1d52d5fb5920370a2a2716cd9a444"}, {"text": "r58:97d32df05c71 (web)", "at_rev": "", "type": "sha", "id": "97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f"}, {"text": "r57:08eaf1451771 (web)", "at_rev": "", "type": "sha", "id": "08eaf14517718dccea4b67755a93368341aca919"}, {"text": "r56:22f71ad26526 (web)", "at_rev": "", "type": "sha", "id": "22f71ad265265a53238359c883aa976e725aa07d"}, {"text": "r49:97501f02b7b4 (web)", "at_rev": "", "type": "sha", "id": "97501f02b7b4330924b647755663a2d90a5e638d"}, {"text": "r47:86ede6754f2b (web)", "at_rev": "", "type": "sha", "id": "86ede6754f2b27309452bb11f997386ae01d0e5a"}, {"text": "r45:014c40c0203c (web)", "at_rev": "", "type": "sha", "id": "014c40c0203c423dc19ecf94644f7cac9d4cdce0"}, {"text": "r30:ee87846a61c1 (default)", "at_rev": "", "type": "sha", "id": "ee87846a61c12153b51543bf860e1026c6d3dcba"}, {"text": "r26:9bb326a04ae5 (default)", "at_rev": "", "type": "sha", "id": "9bb326a04ae5d98d437dece54be04f830cf1edd9"}, {"text": "r24:536c1a194283 (default)", "at_rev": "", "type": "sha", "id": "536c1a19428381cfea92ac44985304f6a8049569"}, {"text": "r8:dc5d2c0661b6 (default)", "at_rev": "", "type": "sha", "id": "dc5d2c0661b61928834a785d3e64a3f80d3aad9c"}, {"text": "r7:3803844fdbd3 (default)", "at_rev": "", "type": "sha", "id": "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"}]}, {"text": "Branches", "children": [{"text": "default", "at_rev": "default", "type": "branch", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}, {"text": "stable", "at_rev": "stable", "type": "branch", "id": "4f7e2131323e0749a740c0a56ab68ae9269c562a"}]}, {"text": "Tags", "children": [{"text": "v0.2.0", "at_rev": "v0.2.0", "type": "tag", "id": "2c96c02def9a7c997f33047761a53943e6254396"}, {"text": "v0.1.9", "at_rev": "v0.1.9", "type": "tag", "id": "8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9"}, {"text": "v0.1.8", "at_rev": "v0.1.8", "type": "tag", "id": "ecb25ba9c96faf1e65a0bc3fd914918420a2f116"}, {"text": "v0.1.7", "at_rev": "v0.1.7", "type": "tag", "id": "f67633a2894edaf28513706d558205fa93df9209"}, {"text": "v0.1.6", "at_rev": "v0.1.6", "type": "tag", "id": "02b38c0eb6f982174750c0e309ff9faddc0c7e12"}, {"text": "v0.1.5", "at_rev": "v0.1.5", "type": "tag", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "v0.1.4", "at_rev": "v0.1.4", "type": "tag", "id": "fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200"}, {"text": "v0.1.3", "at_rev": "v0.1.3", "type": "tag", "id": "17544fbfcd33ffb439e2b728b5d526b1ef30bfcf"}, {"text": "v0.1.2", "at_rev": "v0.1.2", "type": "tag", "id": "a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720"}, {"text": "v0.1.11", "at_rev": "v0.1.11", "type": "tag", "id": "fef5bfe1dc17611d5fb59a7f6f95c55c3606f933"}, {"text": "v0.1.10", "at_rev": "v0.1.10", "type": "tag", "id": "92831aebf2f8dd4879e897024b89d09af214df1c"}, {"text": "v0.1.1", "at_rev": "v0.1.1", "type": "tag", "id": "eb3a60fc964309c1a318b8dfe26aa2d1586c85ae"}, {"text": "tip", "at_rev": "tip", "type": "tag", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}]}], "more": false} No newline at end of file
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r15:16", "type": "sha", "id": "16"}, {"text": "r12:13", "type": "sha", "id": "13"}, {"text": "r7:8", "type": "sha", "id": "8"}, {"text": "r3:4", "type": "sha", "id": "4"}, {"text": "r2:3", "type": "sha", "id": "3"}]}, {"text": "Branches", "children": [{"text": "branches/add-docs", "type": "branch", "id": "branches/add-docs/example.py@26"}, {"text": "branches/argparse", "type": "branch", "id": "branches/argparse/example.py@26"}, {"text": "trunk", "type": "branch", "id": "trunk/example.py@26"}]}, {"text": "Tags", "children": [{"text": "tags/v0.1", "type": "tag", "id": "tags/v0.1/example.py@26"}, {"text": "tags/v0.2", "type": "tag", "id": "tags/v0.2/example.py@26"}, {"text": "tags/v0.3", "type": "tag", "id": "tags/v0.3/example.py@26"}, {"text": "tags/v0.5", "type": "tag", "id": "tags/v0.5/example.py@26"}]}], "more": false} No newline at end of file
1 {"results": [{"text": "Changesets", "children": [{"text": "r15:16", "at_rev": "", "type": "sha", "id": "16"}, {"text": "r12:13", "at_rev": "", "type": "sha", "id": "13"}, {"text": "r7:8", "at_rev": "", "type": "sha", "id": "8"}, {"text": "r3:4", "at_rev": "", "type": "sha", "id": "4"}, {"text": "r2:3", "at_rev": "", "type": "sha", "id": "3"}]}, {"text": "Branches", "children": [{"text": "branches/add-docs", "at_rev": "branches/add-docs", "type": "branch", "id": "26"}, {"text": "branches/argparse", "at_rev": "branches/argparse", "type": "branch", "id": "26"}, {"text": "trunk", "at_rev": "trunk", "type": "branch", "id": "26"}]}, {"text": "Tags", "children": [{"text": "tags/v0.1", "at_rev": "tags/v0.1", "type": "tag", "id": "26"}, {"text": "tags/v0.2", "at_rev": "tags/v0.2", "type": "tag", "id": "26"}, {"text": "tags/v0.3", "at_rev": "tags/v0.3", "type": "tag", "id": "26"}, {"text": "tags/v0.5", "at_rev": "tags/v0.5", "type": "tag", "id": "26"}]}], "more": false} No newline at end of file
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r382:383", "type": "sha", "id": "383"}, {"text": "r323:324", "type": "sha", "id": "324"}, {"text": "r322:323", "type": "sha", "id": "323"}, {"text": "r299:300", "type": "sha", "id": "300"}, {"text": "r277:278", "type": "sha", "id": "278"}, {"text": "r273:274", "type": "sha", "id": "274"}, {"text": "r270:271", "type": "sha", "id": "271"}, {"text": "r269:270", "type": "sha", "id": "270"}, {"text": "r263:264", "type": "sha", "id": "264"}, {"text": "r261:262", "type": "sha", "id": "262"}, {"text": "r251:252", "type": "sha", "id": "252"}, {"text": "r208:209", "type": "sha", "id": "209"}, {"text": "r202:203", "type": "sha", "id": "203"}, {"text": "r173:174", "type": "sha", "id": "174"}, {"text": "r172:173", "type": "sha", "id": "173"}, {"text": "r171:172", "type": "sha", "id": "172"}, {"text": "r145:146", "type": "sha", "id": "146"}, {"text": "r144:145", "type": "sha", "id": "145"}, {"text": "r140:141", "type": "sha", "id": "141"}, {"text": "r134:135", "type": "sha", "id": "135"}, {"text": "r107:108", "type": "sha", "id": "108"}, {"text": "r106:107", "type": "sha", "id": "107"}, {"text": "r100:101", "type": "sha", "id": "101"}, {"text": "r94:95", "type": "sha", "id": "95"}, {"text": "r85:86", "type": "sha", "id": "86"}, {"text": "r73:74", "type": "sha", "id": "74"}, {"text": "r72:73", "type": "sha", "id": "73"}, {"text": "r71:72", "type": "sha", "id": "72"}, {"text": "r69:70", "type": "sha", "id": "70"}, {"text": "r67:68", "type": "sha", "id": "68"}, {"text": "r63:64", "type": "sha", "id": "64"}, {"text": "r62:63", "type": "sha", "id": "63"}, {"text": "r61:62", "type": "sha", "id": "62"}, {"text": "r50:51", "type": "sha", "id": "51"}, {"text": "r49:50", "type": "sha", "id": "50"}, {"text": "r48:49", "type": "sha", "id": "49"}, {"text": "r47:48", "type": "sha", "id": "48"}, {"text": "r46:47", "type": "sha", "id": "47"}, {"text": "r45:46", "type": "sha", "id": "46"}, {"text": "r41:42", "type": "sha", "id": "42"}, {"text": "r39:40", "type": "sha", "id": "40"}, {"text": "r37:38", "type": "sha", "id": "38"}, {"text": "r25:26", "type": "sha", "id": "26"}, {"text": "r23:24", "type": "sha", "id": "24"}, {"text": "r8:9", "type": "sha", "id": "9"}, {"text": "r7:8", "type": "sha", "id": "8"}]}, {"text": "Branches", "children": []}, {"text": "Tags", "children": []}], "more": false} No newline at end of file
1 {"results": [{"text": "Changesets", "children": [{"text": "r382:383", "at_rev": "", "type": "sha", "id": "383"}, {"text": "r323:324", "at_rev": "", "type": "sha", "id": "324"}, {"text": "r322:323", "at_rev": "", "type": "sha", "id": "323"}, {"text": "r299:300", "at_rev": "", "type": "sha", "id": "300"}, {"text": "r277:278", "at_rev": "", "type": "sha", "id": "278"}, {"text": "r273:274", "at_rev": "", "type": "sha", "id": "274"}, {"text": "r270:271", "at_rev": "", "type": "sha", "id": "271"}, {"text": "r269:270", "at_rev": "", "type": "sha", "id": "270"}, {"text": "r263:264", "at_rev": "", "type": "sha", "id": "264"}, {"text": "r261:262", "at_rev": "", "type": "sha", "id": "262"}, {"text": "r251:252", "at_rev": "", "type": "sha", "id": "252"}, {"text": "r208:209", "at_rev": "", "type": "sha", "id": "209"}, {"text": "r202:203", "at_rev": "", "type": "sha", "id": "203"}, {"text": "r173:174", "at_rev": "", "type": "sha", "id": "174"}, {"text": "r172:173", "at_rev": "", "type": "sha", "id": "173"}, {"text": "r171:172", "at_rev": "", "type": "sha", "id": "172"}, {"text": "r145:146", "at_rev": "", "type": "sha", "id": "146"}, {"text": "r144:145", "at_rev": "", "type": "sha", "id": "145"}, {"text": "r140:141", "at_rev": "", "type": "sha", "id": "141"}, {"text": "r134:135", "at_rev": "", "type": "sha", "id": "135"}, {"text": "r107:108", "at_rev": "", "type": "sha", "id": "108"}, {"text": "r106:107", "at_rev": "", "type": "sha", "id": "107"}, {"text": "r100:101", "at_rev": "", "type": "sha", "id": "101"}, {"text": "r94:95", "at_rev": "", "type": "sha", "id": "95"}, {"text": "r85:86", "at_rev": "", "type": "sha", "id": "86"}, {"text": "r73:74", "at_rev": "", "type": "sha", "id": "74"}, {"text": "r72:73", "at_rev": "", "type": "sha", "id": "73"}, {"text": "r71:72", "at_rev": "", "type": "sha", "id": "72"}, {"text": "r69:70", "at_rev": "", "type": "sha", "id": "70"}, {"text": "r67:68", "at_rev": "", "type": "sha", "id": "68"}, {"text": "r63:64", "at_rev": "", "type": "sha", "id": "64"}, {"text": "r62:63", "at_rev": "", "type": "sha", "id": "63"}, {"text": "r61:62", "at_rev": "", "type": "sha", "id": "62"}, {"text": "r50:51", "at_rev": "", "type": "sha", "id": "51"}, {"text": "r49:50", "at_rev": "", "type": "sha", "id": "50"}, {"text": "r48:49", "at_rev": "", "type": "sha", "id": "49"}, {"text": "r47:48", "at_rev": "", "type": "sha", "id": "48"}, {"text": "r46:47", "at_rev": "", "type": "sha", "id": "47"}, {"text": "r45:46", "at_rev": "", "type": "sha", "id": "46"}, {"text": "r41:42", "at_rev": "", "type": "sha", "id": "42"}, {"text": "r39:40", "at_rev": "", "type": "sha", "id": "40"}, {"text": "r37:38", "at_rev": "", "type": "sha", "id": "38"}, {"text": "r25:26", "at_rev": "", "type": "sha", "id": "26"}, {"text": "r23:24", "at_rev": "", "type": "sha", "id": "24"}, {"text": "r8:9", "at_rev": "", "type": "sha", "id": "9"}, {"text": "r7:8", "at_rev": "", "type": "sha", "id": "8"}]}, {"text": "Branches", "children": []}, {"text": "Tags", "children": []}], "more": false} No newline at end of file
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,6 +49,7 b' class TestDeleteScopesDeletesIntegration'
49
49
50 count = 1
50 count = 1
51
51
52
52 def counter():
53 def counter():
53 global count
54 global count
54 val = count
55 val = count
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -52,8 +52,7 b' def test_webhook_parse_url_invalid_event'
52 handler(event, {})
52 handler(event, {})
53
53
54 err = str(err.value)
54 err = str(err.value)
55 assert err.startswith(
55 assert err == "event type `<class 'rhodecode.events.repo.RepoDeleteEvent'>` has no handler defined"
56 'event type `%s` not in supported list' % event.__class__)
57
56
58
57
59 @pytest.mark.parametrize('template,expected_urls', [
58 @pytest.mark.parametrize('template,expected_urls', [
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,7 +1,7 b''
1 import collections
1 import collections
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3
3
4 # Copyright (C) 2010-2019 RhodeCode GmbH
4 # Copyright (C) 2010-2020 RhodeCode GmbH
5 #
5 #
6 # This program is free software: you can redistribute it and/or modify
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License, version 3
7 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -147,7 +147,7 b' def test_cached_perms_data_repository_pe'
147 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
147 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
148
148
149 # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack
149 # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack
150 repo.user_id = User.get_default_user().user_id
150 repo.user_id = User.get_default_user_id()
151
151
152
152
153 def test_cached_perms_data_repository_permissions_not_inheriting_defaults(
153 def test_cached_perms_data_repository_permissions_not_inheriting_defaults(
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -169,7 +169,7 b' class TestPullRequestModel(object):'
169 assert pull_request._last_merge_target_rev is None
169 assert pull_request._last_merge_target_rev is None
170 assert pull_request.last_merge_status is None
170 assert pull_request.last_merge_status is None
171
171
172 status, msg = PullRequestModel().merge_status(pull_request)
172 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
173 assert status is True
173 assert status is True
174 assert msg == 'This pull request can be automatically merged.'
174 assert msg == 'This pull request can be automatically merged.'
175 self.merge_mock.assert_called_with(
175 self.merge_mock.assert_called_with(
@@ -184,7 +184,7 b' class TestPullRequestModel(object):'
184 assert pull_request.last_merge_status is MergeFailureReason.NONE
184 assert pull_request.last_merge_status is MergeFailureReason.NONE
185
185
186 self.merge_mock.reset_mock()
186 self.merge_mock.reset_mock()
187 status, msg = PullRequestModel().merge_status(pull_request)
187 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
188 assert status is True
188 assert status is True
189 assert msg == 'This pull request can be automatically merged.'
189 assert msg == 'This pull request can be automatically merged.'
190 assert self.merge_mock.called is False
190 assert self.merge_mock.called is False
@@ -198,7 +198,7 b' class TestPullRequestModel(object):'
198 assert pull_request._last_merge_target_rev is None
198 assert pull_request._last_merge_target_rev is None
199 assert pull_request.last_merge_status is None
199 assert pull_request.last_merge_status is None
200
200
201 status, msg = PullRequestModel().merge_status(pull_request)
201 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
202 assert status is False
202 assert status is False
203 assert msg == 'This pull request cannot be merged because of merge conflicts. file1'
203 assert msg == 'This pull request cannot be merged because of merge conflicts. file1'
204 self.merge_mock.assert_called_with(
204 self.merge_mock.assert_called_with(
@@ -213,9 +213,9 b' class TestPullRequestModel(object):'
213 assert pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED
213 assert pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED
214
214
215 self.merge_mock.reset_mock()
215 self.merge_mock.reset_mock()
216 status, msg = PullRequestModel().merge_status(pull_request)
216 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
217 assert status is False
217 assert status is False
218 assert msg == 'This pull request cannot be merged because of merge conflicts. '
218 assert msg == 'This pull request cannot be merged because of merge conflicts. file1'
219 assert self.merge_mock.called is False
219 assert self.merge_mock.called is False
220
220
221 def test_merge_status_unknown_failure(self, pull_request):
221 def test_merge_status_unknown_failure(self, pull_request):
@@ -227,7 +227,7 b' class TestPullRequestModel(object):'
227 assert pull_request._last_merge_target_rev is None
227 assert pull_request._last_merge_target_rev is None
228 assert pull_request.last_merge_status is None
228 assert pull_request.last_merge_status is None
229
229
230 status, msg = PullRequestModel().merge_status(pull_request)
230 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
231 assert status is False
231 assert status is False
232 assert msg == (
232 assert msg == (
233 'This pull request cannot be merged because of an unhandled exception. '
233 'This pull request cannot be merged because of an unhandled exception. '
@@ -244,7 +244,7 b' class TestPullRequestModel(object):'
244 assert pull_request.last_merge_status is None
244 assert pull_request.last_merge_status is None
245
245
246 self.merge_mock.reset_mock()
246 self.merge_mock.reset_mock()
247 status, msg = PullRequestModel().merge_status(pull_request)
247 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
248 assert status is False
248 assert status is False
249 assert msg == (
249 assert msg == (
250 'This pull request cannot be merged because of an unhandled exception. '
250 'This pull request cannot be merged because of an unhandled exception. '
@@ -253,7 +253,7 b' class TestPullRequestModel(object):'
253
253
254 def test_merge_status_when_target_is_locked(self, pull_request):
254 def test_merge_status_when_target_is_locked(self, pull_request):
255 pull_request.target_repo.locked = [1, u'12345.50', 'lock_web']
255 pull_request.target_repo.locked = [1, u'12345.50', 'lock_web']
256 status, msg = PullRequestModel().merge_status(pull_request)
256 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
257 assert status is False
257 assert status is False
258 assert msg == (
258 assert msg == (
259 'This pull request cannot be merged because the target repository '
259 'This pull request cannot be merged because the target repository '
@@ -266,7 +266,7 b' class TestPullRequestModel(object):'
266
266
267 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
267 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
268 with patcher:
268 with patcher:
269 status, msg = PullRequestModel().merge_status(pull_request)
269 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
270
270
271 assert status is False
271 assert status is False
272 assert msg == 'Target repository large files support is disabled.'
272 assert msg == 'Target repository large files support is disabled.'
@@ -278,7 +278,7 b' class TestPullRequestModel(object):'
278
278
279 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
279 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
280 with patcher:
280 with patcher:
281 status, msg = PullRequestModel().merge_status(pull_request)
281 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
282
282
283 assert status is False
283 assert status is False
284 assert msg == 'Source repository large files support is disabled.'
284 assert msg == 'Source repository large files support is disabled.'
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,7 +49,7 b" def permissions_setup_func(group_name='g"
49 user_id = test_u1_id
49 user_id = test_u1_id
50 # called by the @with_setup decorator also reset the default user stuff
50 # called by the @with_setup decorator also reset the default user stuff
51 permissions_setup_func(group_name, perm, recursive,
51 permissions_setup_func(group_name, perm, recursive,
52 user_id=User.get_default_user().user_id)
52 user_id=User.get_default_user_id())
53
53
54 # TODO: DRY, compare test_user_group:permissions_setup_func
54 # TODO: DRY, compare test_user_group:permissions_setup_func
55 repo_group = RepoGroup.get_by_group_name(group_name=group_name)
55 repo_group = RepoGroup.get_by_group_name(group_name=group_name)
@@ -142,7 +142,7 b' def test_user_permissions_on_group_with_'
142 # other repos and groups should have this permission now set !
142 # other repos and groups should have this permission now set !
143 recursive = 'all'
143 recursive = 'all'
144 group = 'g0'
144 group = 'g0'
145 default_user_id = User.get_default_user().user_id
145 default_user_id = User.get_default_user_id()
146 permissions_setup_func(group, 'group.write', recursive=recursive,
146 permissions_setup_func(group, 'group.write', recursive=recursive,
147 user_id=default_user_id)
147 user_id=default_user_id)
148
148
@@ -221,7 +221,7 b' def test_user_permissions_on_group_with_'
221 recursive = 'repos'
221 recursive = 'repos'
222 group = 'g0/g0_1'
222 group = 'g0/g0_1'
223 perm = 'group.none'
223 perm = 'group.none'
224 default_user_id = User.get_default_user().user_id
224 default_user_id = User.get_default_user_id()
225
225
226 # TODO: workaround due to different setup calls, adept to py.test style
226 # TODO: workaround due to different setup calls, adept to py.test style
227 permissions_setup_func()
227 permissions_setup_func()
@@ -281,7 +281,7 b' def test_user_permissions_on_group_with_'
281 # should remain intact as we use groups only mode !
281 # should remain intact as we use groups only mode !
282 recursive = 'groups'
282 recursive = 'groups'
283 group = 'g0/g0_1'
283 group = 'g0/g0_1'
284 default_user_id = User.get_default_user().user_id
284 default_user_id = User.get_default_user_id()
285
285
286 # TODO: workaround due to different setup calls, adept to py.test style
286 # TODO: workaround due to different setup calls, adept to py.test style
287 permissions_setup_func()
287 permissions_setup_func()
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,18 +1,89 b''
1 ################################################################################
1 ## -*- coding: utf-8 -*-
2 # RhodeCode VCSServer with HTTP Backend - configuration #
2
3 # #
3 ; #################################
4 ################################################################################
4 ; RHODECODE VCSSERVER CONFIGURATION
5 ; #################################
6
7 [server:main]
8 ; COMMON HOST/IP CONFIG
9 host = 127.0.0.1
10 port = 9900
11
12
13 ; ###########################
14 ; GUNICORN APPLICATION SERVER
15 ; ###########################
16
17 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
18
19 ; Module to use, this setting shouldn't be changed
20 use = egg:gunicorn#main
5
21
6 [app:main]
22 [app:main]
23 ; The %(here)s variable will be replaced with the absolute path of parent directory
24 ; of this file
7 use = egg:rhodecode-vcsserver
25 use = egg:rhodecode-vcsserver
8
26
27 ; Pyramid default locales, we need this to be set
9 pyramid.default_locale_name = en
28 pyramid.default_locale_name = en
10 pyramid.includes =
11
29
12 # default locale used by VCS systems
30 ; default locale used by VCS systems
13 locale = en_US.UTF-8
31 locale = en_US.UTF-8
14
32
15 # cache regions, please don't change
33 ; path to binaries for vcsserver, it should be set by the installer
34 ; at installation time, e.g /home/user/vcsserver-1/profile/bin
35 ; it can also be a path to nix-build output in case of development
36 core.binary_dir = ""
37
38 ; Custom exception store path, defaults to TMPDIR
39 ; This is used to store exception from RhodeCode in shared directory
40 #exception_tracker.store_path =
41
42 ; #############
43 ; DOGPILE CACHE
44 ; #############
45
46 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
47 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
48 cache_dir = %(here)s/data
49
50 ; ***************************************
51 ; `repo_object` cache, default file based
52 ; ***************************************
53
54 ; `repo_object` cache settings for vcs methods for repositories
55 rc_cache.repo_object.backend = dogpile.cache.rc.memory_lru
56
57 ; cache auto-expires after N seconds
58 ; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days)
59 rc_cache.repo_object.expiration_time = 2592000
60
61 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
62 #rc_cache.repo_object.arguments.filename = /tmp/vcsserver_cache.db
63
64 ; ***********************************************************
65 ; `repo_object` cache with redis backend
66 ; recommended for larger instance, and for better performance
67 ; ***********************************************************
68
69 ; `repo_object` cache settings for vcs methods for repositories
70 #rc_cache.repo_object.backend = dogpile.cache.rc.redis_msgpack
71
72 ; cache auto-expires after N seconds
73 ; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days)
74 #rc_cache.repo_object.expiration_time = 2592000
75
76 ; redis_expiration_time needs to be greater then expiration_time
77 #rc_cache.repo_object.arguments.redis_expiration_time = 3592000
78
79 #rc_cache.repo_object.arguments.host = localhost
80 #rc_cache.repo_object.arguments.port = 6379
81 #rc_cache.repo_object.arguments.db = 5
82 #rc_cache.repo_object.arguments.socket_timeout = 30
83 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
84 #rc_cache.repo_object.arguments.distributed_lock = true
85
86 # legacy cache regions, please don't change
16 beaker.cache.regions = repo_object
87 beaker.cache.regions = repo_object
17 beaker.cache.repo_object.type = memorylru
88 beaker.cache.repo_object.type = memorylru
18 beaker.cache.repo_object.max_items = 100
89 beaker.cache.repo_object.max_items = 100
@@ -20,17 +91,13 b' beaker.cache.repo_object.max_items = 100'
20 beaker.cache.repo_object.expire = 300
91 beaker.cache.repo_object.expire = 300
21 beaker.cache.repo_object.enabled = true
92 beaker.cache.repo_object.enabled = true
22
93
23 [server:main]
24 host = 127.0.0.1
25 port = 9900
26
94
27 use = egg:gunicorn#main
28
95
29 ################################
96 ; #####################
30 ### LOGGING CONFIGURATION ####
97 ; LOGGING CONFIGURATION
31 ################################
98 ; #####################
32 [loggers]
99 [loggers]
33 keys = root, vcsserver, beaker
100 keys = root, vcsserver
34
101
35 [handlers]
102 [handlers]
36 keys = console
103 keys = console
@@ -38,9 +105,9 b' keys = console'
38 [formatters]
105 [formatters]
39 keys = generic
106 keys = generic
40
107
41 #############
108 ; #######
42 ## LOGGERS ##
109 ; LOGGERS
43 #############
110 ; #######
44 [logger_root]
111 [logger_root]
45 level = NOTSET
112 level = NOTSET
46 handlers = console
113 handlers = console
@@ -51,27 +118,21 b' handlers ='
51 qualname = vcsserver
118 qualname = vcsserver
52 propagate = 1
119 propagate = 1
53
120
54 [logger_beaker]
55 level = DEBUG
56 handlers =
57 qualname = beaker
58 propagate = 1
59
121
60
122 ; ########
61 ##############
123 ; HANDLERS
62 ## HANDLERS ##
124 ; ########
63 ##############
64
125
65 [handler_console]
126 [handler_console]
66 class = StreamHandler
127 class = StreamHandler
67 args = (sys.stderr,)
128 args = (sys.stderr, )
68 level = DEBUG
129 level = DEBUG
69 formatter = generic
130 formatter = generic
70
131
71 ################
132 ; ##########
72 ## FORMATTERS ##
133 ; FORMATTERS
73 ################
134 ; ##########
74
135
75 [formatter_generic]
136 [formatter_generic]
76 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
137 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
77 datefmt = %Y-%m-%d %H:%M:%S
138 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,4 +1,4 b''
1 # Copyright (C) 2016-2019 RhodeCode GmbH
1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
General Comments 0
You need to be logged in to leave comments. Login now