# HG changeset patch # User Marcin Kuzminski # Date 2020-05-22 09:44:46 # Node ID 77bbe14f480f641ee0edfd7d6a3c79772fe245ea # Parent 5ee65d9a139951aae425796db9c6143ece0ccf25 # Parent edb9d44713f649aa39d04cc454ad6f80696e43f1 release: Merge default into stable for release preparation diff --git a/.bumpversion.cfg b/.bumpversion.cfg --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.18.3 +current_version = 4.19.0 message = release: Bump version {current_version} to {new_version} [bumpversion:file:rhodecode/VERSION] diff --git a/.release.cfg b/.release.cfg --- a/.release.cfg +++ b/.release.cfg @@ -5,25 +5,20 @@ done = false done = true [task:rc_tools_pinned] -done = true [task:fixes_on_stable] -done = true [task:pip2nix_generated] -done = true [task:changelog_updated] -done = true [task:generate_api_docs] -done = true + +[task:updated_translation] [release] -state = prepared -version = 4.18.3 - -[task:updated_translation] +state = in_progress +version = 4.19.0 [task:generate_js_routes] diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ -.PHONY: clean docs docs-clean docs-cleanup test test-clean test-only test-only-postgres test-only-mysql web-build generate-pkgs +.PHONY: clean docs docs-clean docs-cleanup test test-clean test-only test-only-postgres test-only-mysql web-build generate-pkgs pip-packages NODE_PATH=./node_modules WEBPACK=./node_binaries/webpack GRUNT=./node_binaries/grunt - +# set by: PATH_TO_OUTDATED_PACKAGES=/some/path/outdated_packages.py +OUTDATED_PACKAGES = ${PATH_TO_OUTDATED_PACKAGES} clean: make test-clean @@ -55,6 +56,9 @@ web-build: generate-pkgs: nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses" +pip-packages: + python ${OUTDATED_PACKAGES} + generate-js-pkgs: rm -rf node_modules && \ 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" && \ diff --git a/configs/development.ini b/configs/development.ini --- a/configs/development.ini +++ b/configs/development.ini @@ -350,6 +350,17 @@ labs_settings_active = true ; This is used to store exception from RhodeCode in shared directory #exception_tracker.store_path = +; Send email with exception details when it happens +#exception_tracker.send_email = false + +; Comma separated list of recipients for exception emails, +; e.g admin@rhodecode.com,devops@rhodecode.com +; Can be left empty, then emails will be sent to ALL super-admins +#exception_tracker.send_email_recipients = + +; optional prefix to Add to email Subject +#exception_tracker.email_prefix = [RHODECODE ERROR] + ; File store configuration. This is used to store and serve uploaded files file_store.enabled = true @@ -605,8 +616,9 @@ vcs.backends = hg, git, svn vcs.connection_timeout = 3600 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out. -; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible -#vcs.svn.compatible_version = pre-1.8-compatible +; Set a numeric version for your current SVN e.g 1.8, or 1.12 +; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible +#vcs.svn.compatible_version = 1.8 ; #################################################### diff --git a/configs/production.ini b/configs/production.ini --- a/configs/production.ini +++ b/configs/production.ini @@ -301,6 +301,17 @@ labs_settings_active = true ; This is used to store exception from RhodeCode in shared directory #exception_tracker.store_path = +; Send email with exception details when it happens +#exception_tracker.send_email = false + +; Comma separated list of recipients for exception emails, +; e.g admin@rhodecode.com,devops@rhodecode.com +; Can be left empty, then emails will be sent to ALL super-admins +#exception_tracker.send_email_recipients = + +; optional prefix to Add to email Subject +#exception_tracker.email_prefix = [RHODECODE ERROR] + ; File store configuration. This is used to store and serve uploaded files file_store.enabled = true @@ -556,8 +567,9 @@ vcs.backends = hg, git, svn vcs.connection_timeout = 3600 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out. -; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible -#vcs.svn.compatible_version = pre-1.8-compatible +; Set a numeric version for your current SVN e.g 1.8, or 1.12 +; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible +#vcs.svn.compatible_version = 1.8 ; #################################################### diff --git a/docs/admin/repo_admin/repo-admin-tasks.rst b/docs/admin/repo_admin/repo-admin-tasks.rst --- a/docs/admin/repo_admin/repo-admin-tasks.rst +++ b/docs/admin/repo_admin/repo-admin-tasks.rst @@ -59,3 +59,46 @@ exit the ishell after the execution:: echo "%run repo_delete_task.py" | rccontrol ishell enterprise-1 + + + +Bulk edit permissions for all repositories or groups +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In case when a permissions should be applied in bulk here are two ways to apply +the permissions onto *all* repositories and/or repository groups. + +1) Start by running the interactive ishell interface + +.. code-block:: bash + :dedent: 1 + + # starts the ishell interactive prompt + $ rccontrol ishell enterprise-1 + + +2a) Add user called 'admin' into all repositories with write permission. + Permissions can be also `repository.read`, `repository.admin`, `repository.none` + +.. code-block:: python + :dedent: 1 + + In [1]: from rhodecode.model.repo import RepoModel + In [2]: user = User.get_by_username('admin') + In [3]: permission_name = 'repository.write' + In [4]: for repo in Repository.get_all(): + ...: RepoModel().grant_user_permission(repo, user, permission_name) + ...: Session().commit() + +2b) Add user called 'admin' into all repository groups with write permission. + Permissions can be also can be `group.read`, `group.admin`, `group.none` + +.. code-block:: python + :dedent: 1 + + In [1]: from rhodecode.model.repo import RepoModel + In [2]: user = User.get_by_username('admin') + In [3]: permission_name = 'group.write' + In [4]: for repo_group in RepoGroup.get_all(): + ...: RepoGroupModel().grant_user_permission(repo_group, user, permission_name) + ...: Session().commit() \ No newline at end of file diff --git a/docs/admin/system-overview.rst b/docs/admin/system-overview.rst --- a/docs/admin/system-overview.rst +++ b/docs/admin/system-overview.rst @@ -22,8 +22,9 @@ Supported Operating Systems Linux ^^^^^ -* Ubuntu 14.04 -* CentOS 6.2 and 7 +* Ubuntu 14.04+ +* CentOS 6.2, 7 and 8 +* RHEL 6.2, 7 and 8 * Debian 7.8 * RedHat Fedora * Arch Linux diff --git a/docs/api/methods/pull-request-methods.rst b/docs/api/methods/pull-request-methods.rst --- a/docs/api/methods/pull-request-methods.rst +++ b/docs/api/methods/pull-request-methods.rst @@ -39,7 +39,7 @@ close_pull_request comment_pull_request -------------------- -.. py:function:: comment_pull_request(apiuser, pullrequestid, repoid=, message=, commit_id=, status=, comment_type=, resolves_comment_id=, extra_recipients=, userid=>) +.. py:function:: comment_pull_request(apiuser, pullrequestid, repoid=, message=, commit_id=, status=, comment_type=, resolves_comment_id=, extra_recipients=, userid=>, send_email=) Comment on the pull request specified with the `pullrequestid`, in the |repo| specified by the `repoid`, and optionally change the @@ -70,6 +70,8 @@ comment_pull_request :type extra_recipients: Optional(list) :param userid: Comment on the pull request as this user :type userid: Optional(str or int) + :param send_email: Define if this comment should also send email notification + :type send_email: Optional(bool) Example output: @@ -160,6 +162,7 @@ get_pull_request "status" : "", "created_on": "", "updated_on": "", + "versions": "", "commit_ids": [ ... "", @@ -249,7 +252,9 @@ get_pull_request_comments }, "comment_text": "Example text", "comment_type": null, - "pull_request_version": null + "pull_request_version": null, + "comment_commit_id": None, + "comment_pull_request_id": } ], error : null diff --git a/docs/api/methods/repo-methods.rst b/docs/api/methods/repo-methods.rst --- a/docs/api/methods/repo-methods.rst +++ b/docs/api/methods/repo-methods.rst @@ -28,7 +28,7 @@ add_field_to_repo comment_commit -------------- -.. py:function:: comment_commit(apiuser, repoid, commit_id, message, status=, comment_type=, resolves_comment_id=, extra_recipients=, userid=>) +.. py:function:: comment_commit(apiuser, repoid, commit_id, message, status=, comment_type=, resolves_comment_id=, extra_recipients=, userid=>, send_email=) Set a commit comment, and optionally change the status of the commit. @@ -52,6 +52,8 @@ comment_commit :type extra_recipients: Optional(list) :param userid: Set the user name of the comment creator. :type userid: Optional(str or int) + :param send_email: Define if this comment should also send email notification + :type send_email: Optional(bool) Example error output: diff --git a/docs/api/methods/search-methods.rst b/docs/api/methods/search-methods.rst --- a/docs/api/methods/search-methods.rst +++ b/docs/api/methods/search-methods.rst @@ -3,6 +3,21 @@ search methods ============== +get_audit_logs +-------------- + +.. py:function:: get_audit_logs(apiuser, query) + + return full audit logs based on the query. + + Please see `example query in admin > settings > audit logs` for examples + + :param apiuser: This is filled automatically from the |authtoken|. + :type apiuser: AuthUser + :param query: filter query, example: action:repo.artifact.add date:[20200401 TO 20200601]" + :type query: str + + search ------ diff --git a/docs/auth/auth-ldap-groups.rst b/docs/auth/auth-ldap-groups.rst --- a/docs/auth/auth-ldap-groups.rst +++ b/docs/auth/auth-ldap-groups.rst @@ -68,7 +68,7 @@ Below is example setup that can be used # Multiple servers can be specified using commas *option*: `port` => `389` - # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL) + # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL) *option*: `timeout` => `300` # Timeout for LDAP connection diff --git a/docs/auth/auth-ldap.rst b/docs/auth/auth-ldap.rst --- a/docs/auth/auth-ldap.rst +++ b/docs/auth/auth-ldap.rst @@ -58,7 +58,7 @@ Below is example setup that can be used # Multiple servers can be specified using commas *option*: `port` => `389` - # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL) + # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL) *option*: `timeout` => `300` # Timeout for LDAP connection diff --git a/docs/auth/ldap-active-directory.rst b/docs/auth/ldap-active-directory.rst --- a/docs/auth/ldap-active-directory.rst +++ b/docs/auth/ldap-active-directory.rst @@ -22,7 +22,7 @@ authentication:: # Multiple servers can be specified using commas *option*: `port` => `389` - # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL) + # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL) *option*: `timeout` => `300` # Timeout for LDAP connection diff --git a/docs/contributing/dev-setup.rst b/docs/contributing/dev-setup.rst --- a/docs/contributing/dev-setup.rst +++ b/docs/contributing/dev-setup.rst @@ -19,7 +19,7 @@ Setup Nix Package Manager To install the Nix Package Manager, please run:: - $ curl https://nixos.org/releases/nix/nix-2.0.4/install | sh + $ curl https://releases.nixos.org/nix/nix-2.3.4/install | sh or go to https://nixos.org/nix/ and follow the installation instructions. Once this is correctly set up on your system, you should be able to use the @@ -47,12 +47,12 @@ Switch nix to the latest STABLE channel run:: - nix-channel --add https://nixos.org/channels/nixos-18.03 nixpkgs + nix-channel --add https://nixos.org/channels/nixos-20.03 nixpkgs Followed by:: nix-channel --update - nix-env -i nix-2.0.4 + nix-env -i nix-2.3.4 Install required binaries diff --git a/docs/known-issues/known-issues.rst b/docs/known-issues/known-issues.rst --- a/docs/known-issues/known-issues.rst +++ b/docs/known-issues/known-issues.rst @@ -3,20 +3,6 @@ Known Issues ============ -Subversion Issues ------------------ - -Limited |svn| support has been achieved for this release, -|release|. The following known issues are in development for improvement. - -* |svn| |repo| creation: - Terminating the VCS Server during remote importation of |svn| |repos| leaves - the the process still running in the background. - -* |svn| |repo| checkin/checkout: - |svn| cloning support is not enabled by default. Please contact support if - you want it enabled. - Windows Upload -------------- @@ -49,12 +35,16 @@ shipped with the optimal configuration a To fix this issue, upgrade to |RCE| 3.3.2 or greater, and if you discover memory consumption issues check the VCS Server settings. -Fedora 23 / Ubuntu 18.04 ------------------------- +Newer Operating system locales +------------------------------ |RCC| has a know problem with locales, due to changes in glibc 2.27+ which affects the local-archive format, which is now incompatible with our used glibc 2.26. +Mostly affected are: +- Fedora 23+ +- Ubuntu 18.04 +- CentOS / RHEL 8 To work around this problem, you need set path to ``$LOCAL_ARCHIVE`` to the locale package in older pre glibc 2.27 format, or set `LC_ALL=C` in your enviroment. @@ -74,8 +64,15 @@ 2. Point ``$LOCAL_ARCHIVE`` to the local $ export LOCALE_ARCHIVE=/home/USER/locale-archive # change to your path -This can either added in `~/.rccontrol/supervisor/supervisord.ini` -or in user .bashrc/.zshrc etc, or via a startup script that +This should be added *both* in `enviroment` variable of `~/.rccontrol/supervisor/supervisord.ini` +e.g + +``` +[supervisord] +environment = HOME=/home/user/rhodecode,LOCALE_ARCHIVE=/YOUR-PATH/locale-archive` +``` + +and in user .bashrc/.zshrc etc, or via a startup script that runs `rccontrol self-init` If you happen to be running |RCC| from systemd, use the following diff --git a/docs/release-notes/release-notes-4.19.0.rst b/docs/release-notes/release-notes-4.19.0.rst new file mode 100644 --- /dev/null +++ b/docs/release-notes/release-notes-4.19.0.rst @@ -0,0 +1,156 @@ +|RCE| 4.19.0 |RNS| +------------------ + +Release Date +^^^^^^^^^^^^ + +- 2020-05-22 + + +New Features +^^^^^^^^^^^^ + +- Pull requests: add information about changes in source repositories in pull-request show page. + Fixes #5611, Fixes #5561 + Added new preview for size (commits/files) of PRs before opening, this is now based + on the special logic that calculates common ancestor and has access to preview diff + Store common ancestor in DB so updates of pull-requests are consistent +- Pull requests: fixed case for GIT repositories when a merge check failed due to + merge conflicts the pull request wrongly reported missing commits. + we're now searching for dangling commits in a repo that has them and cannot see them + because of failed merge checks. +- Pull requests: allow filter displayed results by author +- Pull requests: added filters to my account pull requests page. +- Quick search: added ability to search for pull-requests using `pr:` prefix. + Permissions are checked against the access to target repositories, and users + can now search for pull request number, description or title. +- UI: replaced js prompts with sweet-alert prompts. +- SVN: bumped shipped SVN to 1.13.0 release. +- Integration Hooks: added new hooks for comments on pull requests and commits. + Allows writing custom actions on top of commenting. + E.g `@CI-BOT re-test` could trigger CI job to re-test a pull requests or commit. + Added new rcextension hooks, Fixes #5583, and added examples on how to trigger CI build on certain comments. +- Exception tracker: added possibility to send notification email when server encountered an unhandled exception. + new .ini file flag: `exception_tracker.send_email = false` and `exception_tracker.send_email_recipients =` + can be set to enable this function. +- Mercurial: enable enhanced diffs for Mercurial that show context of changed functions inside the diff. + This makes diff actually more consistent with how GIT backend shows them. Fixes #5614 + + +General +^^^^^^^ + +- Pull requests: fixed small UI glitches in pull request view. +- System Info: Python packages now expose the package location info. +- API: added new flag to comment api to disable email sending when creating a comment. +- Exceptions: don't report lookup errors as exceptions stored in the exception store. + Those are regular not found problems that don't indicate any exceptional case + also make the errors report nicer, not as KeyError, or generic Exception +- Exception tracker: store request info if available to track which URL caused an error. +- Integrations: handle new commenting events and optimize calls for Jira/Redmine + Speed up issue fetching by optimizing how Jira/Redmine client behaves + For redmine we don't iterate issues anymore which is *much* faster, and makes pushes with tickets faster. +- SVN: allow to specify any svn compatible version string not only hardcoded values. + The new SVN code allows to specify this by numeric values now. e.g 1.13 etc. + Fixes #5605. +- Emails: added `premailer` parsing for inline style formatting to make emails render + nicer on all email clients. +- Repositories: switched repo type selector to radio buttons and preserve order of + enabled backends inside .ini files. +- Repositories: show recommendation for updating hooks if they are outdated. +- Files: add pre-commit checks on file edit/delete/add operations. This prevents + loosing content while editing when repositories changes during those operations. + Fixes #5607. +- Files: changed the copy path label to indicate we're actually copying only the path. + Added copy permalink helper to copy the url quickly. Fixes #5602 +- LDAP: updated ldap plugin to help with debug and support by extending logging and + improving error messages. +- LDAP: fixed example LDAPs port. +- Dependencies: bump redmine client. +- Dependencies: bumped bleach==3.1.3 +- Dependencies: bumped webtest==2.0.34 +- Dependencies: bumped packaging==20.3 +- Dependencies: bumped pyparsing==2.4.7 +- Dependencies: bumped sqlalchemy==1.3.15 +- Dependencies: bumped hupper==1.10.2 +- Dependencies: bumped alembic==1.4.2 +- Dependencies: bumped wcwidth==0.1.9 +- Dependencies: bumped python-ldap==3.2.0 +- Dependencies: bumped importlib-metadata==1.5.0 +- Dependencies: bumped redis==3.4.1 +- Dependencies: bumped importlib-metadata==1.6.0 +- Dependencies: bumped pytz==2019.3 +- Dependencies: bumped paste==3.4.0 +- Dependencies: bumped weberror==0.13.1 +- Dependencies: bumped pyparsing==2.4.6 +- Dependencies: bumped ipdb==0.13.2 +- Dependencies: bumped pastedeploy==2.1.0 +- Dependencies: bumped docutils==0.16.0 +- Dependencies: bumped pyramid-debugtoolbar==4.6.1 +- Dependencies: bumped gevent==1.5.0 +- Dependencies: bumped psutil==5.7.0 + + +Security +^^^^^^^^ + +- Logging: expose usernames in the logs for each request made to RhodeCode. + This enables auditing capabilities for all actions against the web interface. +- Users: increased security on the way we're displaying authentication tokens. + We don't expose all on single page. Request needs a validation before viewing of each token. +- Logging: added some nicer logging for file path ACL checks. +- Audit Log: extend the commit api data with references to commit_id or pull_request_id. + This allows tracking those in the audit-logs. + + +Performance +^^^^^^^^^^^ + +- Exception Tracker: optimized the check for smtp_server before doing heavy lifting + of exception email sending. +- Auth: enabled cache flags for fetching ACL ids. + Those are now safe to cache since we have a proper cache invalidation logic for + permissions of users, for lots of repo this makes our goto switcher much much faster. +- Application: use simpler way to extract default_user_id, this will be now registered + at server boot, reducing number of times we fetch this from database. +- Pull requests: changed reviewers metadata function for optimizing the diff calculations. + We're now doing a single request to calculate reviewers and diff preview instead of twice like before. + + +Fixes +^^^^^ + +- GIT: fixed readme searcher for Git repositories using libgit2 and non-ascii directories. +- Full text search: fixed error while highlighting special search terms e.g 'enabled \= ' +- Full text search: fixed problems with non-ascii files indexing. +- Diffs: improve text on unresolved comments attached to files that no longer exist in the review. + Fixes #5615. +- Auth: fixed generation of authomatic secrets for new plugins. +- Auth: failsafe github auth if it doesn't provide full name for users. +- Permissions: fixed problem with permissions changes from permission page due to missing cache flush. + This caused certain permission changed be visible after some time of the edit. + We now ensure *all* caches used for permissions are properly flushed right after the change. +- SVN: explicitly specify tunnel-user to properly map rhodecode username on svn commit + via SSH backend. Fixes #5608. +- SVN: fixed case of wrong extracted repository name for SSH backend. In cases + where we commit to a nested subdirs SVN reported the access path with the subdir paths in it. + We couldn't then match that extended name into proper rhodecode repository for ACL checks. + Current implementation gives an slight overhead as we have to lookup all repositories. + Fixes #5606 +- SVN: fixed problem with special characters inside subdirectories. +- SVN: fixed SVN refs switcher on files that used old format of diff url. Fixes #5599, #5610 +- Search: remove excessive quoting on search pagination. Fixes #5604 +- File browser: fixed the repo switcher `?at=` flag being lost when walking on the file tree. +- File browser: fixed unicode problems on image preview, and make images center, no-stretch. +- DB migrations: fixed db migrate for latest sqlite version. +- Feed generator: fixed missing utc definition that could cause server 500 error. + + +Upgrade notes +^^^^^^^^^^^^^ + +- RhodeCode has been tested on CentOS/RHEL 8 and we added those as officially supported platforms. +- This release introduces lots of optimizations and changes how the pull requests reviewers, + and diff preview is made. We cut the pull request creation time by 50%. + Please look closer to this new logic na report any potential problems with this change. +- SVN was bumped to 1.13 version. \ No newline at end of file diff --git a/docs/release-notes/release-notes.rst b/docs/release-notes/release-notes.rst --- a/docs/release-notes/release-notes.rst +++ b/docs/release-notes/release-notes.rst @@ -9,6 +9,7 @@ Release Notes .. toctree:: :maxdepth: 1 + release-notes-4.19.0.rst release-notes-4.18.3.rst release-notes-4.18.2.rst release-notes-4.18.1.rst diff --git a/docs/requirements_docs.txt b/docs/requirements_docs.txt --- a/docs/requirements_docs.txt +++ b/docs/requirements_docs.txt @@ -1,7 +1,7 @@ sphinx==1.8.2 six==1.11.0 sphinx_rtd_theme==0.4.1 -docutils==0.14.0 +docutils==0.16.0 pygments==2.3.0 markupsafe==1.0.0 jinja2==2.9.6 diff --git a/grunt_config.json b/grunt_config.json --- a/grunt_config.json +++ b/grunt_config.json @@ -35,7 +35,8 @@ "<%= dirs.js.node_modules %>/moment/min/moment.min.js", "<%= dirs.js.node_modules %>/clipboard/dist/clipboard.min.js", "<%= dirs.js.node_modules %>/favico.js/favico-0.3.10.min.js", - "<%= dirs.js.node_modules %>/dropzone/dist/dropzone.js", + "<%= dirs.js.node_modules %>/dropzone/dist/min/dropzone.min.js", + "<%= dirs.js.node_modules %>/sweetalert2/dist/sweetalert2.min.js", "<%= dirs.js.node_modules %>/sticky-sidebar/dist/sticky-sidebar.min.js", "<%= dirs.js.node_modules %>/sticky-sidebar/dist/jquery.sticky-sidebar.min.js", "<%= dirs.js.node_modules %>/waypoints/lib/noframework.waypoints.min.js", diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "grunt-contrib-watch": "^0.6.1", "grunt-webpack": "^3.1.3", "grunt-contrib-uglify": "^4.0.1", + "sweetalert2": "^9.10.12", "jquery": "1.11.3", "mark.js": "8.11.1", "jshint": "^2.9.1-rc3", diff --git a/pkgs/node-packages.nix b/pkgs/node-packages.nix --- a/pkgs/node-packages.nix +++ b/pkgs/node-packages.nix @@ -49,13 +49,13 @@ let sha512 = "7+TPEAfWsRdhj1Y8UeF1759ktpVu+c3sG16rJiUC3wF9+woQ9xI1zUm2d59i7Yc3aDEJrR/Q8Y262KlOvyGVNg=="; }; }; - "@polymer/iron-autogrow-textarea-3.0.1" = { + "@polymer/iron-autogrow-textarea-3.0.3" = { name = "_at_polymer_slash_iron-autogrow-textarea"; packageName = "@polymer/iron-autogrow-textarea"; - version = "3.0.1"; - src = fetchurl { - url = "https://registry.npmjs.org/@polymer/iron-autogrow-textarea/-/iron-autogrow-textarea-3.0.1.tgz"; - sha512 = "FgSL7APrOSL9Vu812sBCFlQ17hvnJsBAV2C2e1UAiaHhB+dyfLq8gGdGUpqVWuGJ50q4Y/49QwCNnLf85AdVYA=="; + version = "3.0.3"; + src = fetchurl { + url = "https://registry.npmjs.org/@polymer/iron-autogrow-textarea/-/iron-autogrow-textarea-3.0.3.tgz"; + sha512 = "5r0VkWrIlm0JIp5E5wlnvkw7slK72lFRZXncmrsLZF+6n1dg2rI8jt7xpFzSmUWrqpcyXwyKaGaDvUjl3j4JLA=="; }; }; "@polymer/iron-behaviors-3.0.1" = { @@ -76,13 +76,13 @@ let sha512 = "aDr0cbCNVq49q+pOqa6CZutFh+wWpwPMLpEth9swx+GkAj+gCURhuQkaUYhIo5f2egDbEioR1aeHMnPlU9dQZA=="; }; }; - "@polymer/iron-fit-behavior-3.0.1" = { + "@polymer/iron-fit-behavior-3.0.2" = { name = "_at_polymer_slash_iron-fit-behavior"; packageName = "@polymer/iron-fit-behavior"; - version = "3.0.1"; - src = fetchurl { - url = "https://registry.npmjs.org/@polymer/iron-fit-behavior/-/iron-fit-behavior-3.0.1.tgz"; - sha512 = "/M0B1L30k31vmwNBaGuZcxzUAhJSHoGccb/DF0CDKI/hT8UlkTvcyemaWdOpmHHLgY52ceKIkRwA3AeXrKyvaQ=="; + version = "3.0.2"; + src = fetchurl { + url = "https://registry.npmjs.org/@polymer/iron-fit-behavior/-/iron-fit-behavior-3.0.2.tgz"; + sha512 = "JndryJYbBR3gSN5IlST4rCHsd01+OyvYpRO6z5Zd3C6u5V/m07TwAtcf3aXwZ8WBNt2eLG28OcvdSO7XR2v2pg=="; }; }; "@polymer/iron-flex-layout-3.0.1" = { @@ -112,13 +112,13 @@ let sha512 = "pWguPugiLYmWFV9UWxLWzZ6gm4wBwQdDy4VULKwdHCqR7OP7u98h+XDdGZsSlDPv6qoryV/e3tGHlTIT0mbzJA=="; }; }; - "@polymer/iron-overlay-behavior-3.0.2" = { + "@polymer/iron-overlay-behavior-3.0.3" = { name = "_at_polymer_slash_iron-overlay-behavior"; packageName = "@polymer/iron-overlay-behavior"; - version = "3.0.2"; - src = fetchurl { - url = "https://registry.npmjs.org/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.2.tgz"; - sha512 = "j1qmt6mJHCwpe1mKOvqK5kcCUPQr5LSrlqpgRDbUuLgUfNJ/vGTipjrkBlfbEUagm5FEQdc1VLPLSQP6WVuP9g=="; + version = "3.0.3"; + src = fetchurl { + url = "https://registry.npmjs.org/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.3.tgz"; + sha512 = "Q/Fp0+uOQQ145ebZ7T8Cxl4m1tUKYjyymkjcL2rXUm+aDQGb1wA1M1LYxUF5YBqd+9lipE0PTIiYwA2ZL/sznA=="; }; }; "@polymer/iron-resizable-behavior-3.0.1" = { @@ -157,13 +157,13 @@ let sha512 = "JRNBc+Oj9EWnmyLr7FcCr8T1KAnEHPh6mosln9BUdkM+qYaYsudSICh3cjTIbnj6AuF5OJidoLkM1dlyj0j6Zg=="; }; }; - "@polymer/paper-ripple-3.0.1" = { + "@polymer/paper-ripple-3.0.2" = { name = "_at_polymer_slash_paper-ripple"; packageName = "@polymer/paper-ripple"; - version = "3.0.1"; - src = fetchurl { - url = "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.1.tgz"; - sha512 = "dgOe12GyCF1VZBLUQqnzGWlf3xb255FajNCVB1VFj/AtskYtoamnafa7m3a+1vs+C8qbg4Benn5KwgxVDSW4cg=="; + version = "3.0.2"; + src = fetchurl { + url = "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.2.tgz"; + sha512 = "DnLNvYIMsiayeICroYxx6Q6Hg1cUU8HN2sbutXazlemAlGqdq80qz3TIaVdbpbt/pvjcFGX2HtntMlPstCge8Q=="; }; }; "@polymer/paper-spinner-3.0.2" = { @@ -211,13 +211,13 @@ let sha512 = "yiUk09opTEnE1lK+tb501ENb+yQBi4p++Ep0eGJAHesVYKVMPNgPphVKkIizkDaU+n0SE+zXfTsRbYyOMDYXSg=="; }; }; - "@polymer/polymer-3.3.0" = { + "@polymer/polymer-3.3.1" = { name = "_at_polymer_slash_polymer"; packageName = "@polymer/polymer"; - version = "3.3.0"; - src = fetchurl { - url = "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.3.0.tgz"; - sha512 = "rij7suomS7DxdBamnwr/Xa0V5hpypf7I9oYKseF2FWz5Xh2a3wJNpVjgJy1adXVCxqIyPhghsrthnfCt7EblsQ=="; + version = "3.3.1"; + src = fetchurl { + url = "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.3.1.tgz"; + sha512 = "8KaB48tzyMjdsHdxo5KvCAaqmTe7rYDzQAoj/pyEfq9Fp4YfUaS+/xqwYj0GbiDAUNzwkmEQ7dw9cgnRNdKO8A=="; }; }; "@types/clone-0.1.30" = { @@ -229,13 +229,13 @@ let sha1 = "e7365648c1b42136a59c7d5040637b3b5c83b614"; }; }; - "@types/node-6.14.9" = { + "@types/node-6.14.10" = { name = "_at_types_slash_node"; packageName = "@types/node"; - version = "6.14.9"; - src = fetchurl { - url = "https://registry.npmjs.org/@types/node/-/node-6.14.9.tgz"; - sha512 = "leP/gxHunuazPdZaCvsCefPQxinqUDsCxCR5xaDUrY2MkYxQRFZZwU5e7GojyYsGB7QVtCi7iVEl/hoFXQYc+w=="; + version = "6.14.10"; + src = fetchurl { + url = "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz"; + sha512 = "pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA=="; }; }; "@types/parse5-2.2.34" = { @@ -409,22 +409,22 @@ let sha512 = "mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA=="; }; }; - "@webcomponents/shadycss-1.9.2" = { + "@webcomponents/shadycss-1.9.6" = { name = "_at_webcomponents_slash_shadycss"; packageName = "@webcomponents/shadycss"; - version = "1.9.2"; - src = fetchurl { - url = "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.9.2.tgz"; - sha512 = "GsD7RpDVrVdgC6e+D8zQia8RGNmEGQ9/qotnVPQYPrIXhGS5xSt6ZED9YmuHz3HbLqY+E54tE1EK3tjLzSCGrw=="; - }; - }; - "@webcomponents/webcomponentsjs-2.3.0" = { + version = "1.9.6"; + src = fetchurl { + url = "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.9.6.tgz"; + sha512 = "5fFjvP0jQJZoXK6YzYeYcIDGJ5oEsdjr1L9VaYLw5yxNd4aRz4srMpwCwldeNG0A6Hvr9igbG7fCsBeiiCXd7A=="; + }; + }; + "@webcomponents/webcomponentsjs-2.4.3" = { name = "_at_webcomponents_slash_webcomponentsjs"; packageName = "@webcomponents/webcomponentsjs"; - version = "2.3.0"; - src = fetchurl { - url = "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.3.0.tgz"; - sha512 = "sR6FOrNnnncRuoJDqq9QxtRsJMbIvASw4vnJwIYKVlKO3AMc+NAr/bIQNnUiTTE9pBDTJkFpVaUdjJaRdsjmyA=="; + version = "2.4.3"; + src = fetchurl { + url = "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.4.3.tgz"; + sha512 = "cV4+sAmshf8ysU2USutrSRYQkJzEYKHsRCGa0CkMElGpG5747VHtkfsW3NdVIBV/m2MDKXTDydT4lkrysH7IFA=="; }; }; "@xtuc/ieee754-1.2.0" = { @@ -463,13 +463,13 @@ let sha1 = "45e37fb39e8da3f25baee3ff5369e2bb5f22017a"; }; }; - "acorn-5.7.3" = { + "acorn-5.7.4" = { name = "acorn"; packageName = "acorn"; - version = "5.7.3"; - src = fetchurl { - url = "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz"; - sha512 = "T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="; + version = "5.7.4"; + src = fetchurl { + url = "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz"; + sha512 = "1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="; }; }; "acorn-dynamic-import-3.0.0" = { @@ -499,13 +499,13 @@ let sha1 = "82ffb02b29e662ae53bdc20af15947706739c536"; }; }; - "ajv-6.10.2" = { + "ajv-6.12.0" = { name = "ajv"; packageName = "ajv"; - version = "6.10.2"; - src = fetchurl { - url = "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz"; - sha512 = "TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw=="; + version = "6.12.0"; + src = fetchurl { + url = "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz"; + sha512 = "D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw=="; }; }; "ajv-keywords-3.4.1" = { @@ -860,13 +860,13 @@ let sha1 = "14342dd38dbcc94d0e5b87d763cd63612c0e794f"; }; }; - "aws4-1.8.0" = { + "aws4-1.9.1" = { name = "aws4"; packageName = "aws4"; - version = "1.8.0"; - src = fetchurl { - url = "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz"; - sha512 = "ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="; + version = "1.9.1"; + src = fetchurl { + url = "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz"; + sha512 = "wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="; }; }; "babel-code-frame-6.26.0" = { @@ -1445,13 +1445,22 @@ let sha512 = "Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="; }; }; - "bluebird-3.7.1" = { + "bindings-1.5.0" = { + name = "bindings"; + packageName = "bindings"; + version = "1.5.0"; + src = fetchurl { + url = "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz"; + sha512 = "p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="; + }; + }; + "bluebird-3.7.2" = { name = "bluebird"; packageName = "bluebird"; - version = "3.7.1"; - src = fetchurl { - url = "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz"; - sha512 = "DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg=="; + version = "3.7.2"; + src = fetchurl { + url = "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz"; + sha512 = "XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="; }; }; "bn.js-4.11.8" = { @@ -1580,13 +1589,13 @@ let sha512 = "WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ=="; }; }; - "buffer-4.9.1" = { + "buffer-4.9.2" = { name = "buffer"; packageName = "buffer"; - version = "4.9.1"; - src = fetchurl { - url = "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz"; - sha1 = "6d1bb601b07a4efced97094132093027c95bc298"; + version = "4.9.2"; + src = fetchurl { + url = "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz"; + sha512 = "xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="; }; }; "buffer-from-1.1.1" = { @@ -1670,22 +1679,22 @@ let sha1 = "b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"; }; }; - "caniuse-db-1.0.30001006" = { + "caniuse-db-1.0.30001042" = { name = "caniuse-db"; packageName = "caniuse-db"; - version = "1.0.30001006"; - src = fetchurl { - url = "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001006.tgz"; - sha512 = "Xn25grc0GXATFnnEX+KP3IwEv6ZdHs4CALyLKvK8pBeeBe+hSpqy3/GyKBgEp4hn6o+bI+GNeNeQBf9PBOK0EQ=="; - }; - }; - "caniuse-lite-1.0.30001006" = { + version = "1.0.30001042"; + src = fetchurl { + url = "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001042.tgz"; + sha512 = "2RKrB2hkLCW/8Uj32oaXj0O+N9ROo0/BF0EueWHwgs6AeeSiL+rCSsbICR3ayBJOZavgcFx65ZCw7QiafsoUFQ=="; + }; + }; + "caniuse-lite-1.0.30001042" = { name = "caniuse-lite"; packageName = "caniuse-lite"; - version = "1.0.30001006"; - src = fetchurl { - url = "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001006.tgz"; - sha512 = "MXnUVX27aGs/QINz+QG1sWSLDr3P1A3Hq5EUWoIt0T7K24DuvMxZEnh3Y5aHlJW6Bz2aApJdSewdYLd8zQnUuw=="; + version = "1.0.30001042"; + src = fetchurl { + url = "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz"; + sha512 = "igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw=="; }; }; "caseless-0.12.0" = { @@ -1742,13 +1751,13 @@ let sha512 = "ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg=="; }; }; - "chownr-1.1.3" = { + "chownr-1.1.4" = { name = "chownr"; packageName = "chownr"; - version = "1.1.3"; - src = fetchurl { - url = "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz"; - sha512 = "i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="; + version = "1.1.4"; + src = fetchurl { + url = "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz"; + sha512 = "jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="; }; }; "chrome-trace-event-1.0.2" = { @@ -1787,13 +1796,13 @@ let sha512 = "qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg=="; }; }; - "clean-css-4.2.1" = { + "clean-css-4.2.3" = { name = "clean-css"; packageName = "clean-css"; - version = "4.2.1"; - src = fetchurl { - url = "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz"; - sha512 = "4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g=="; + version = "4.2.3"; + src = fetchurl { + url = "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz"; + sha512 = "VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA=="; }; }; "cli-1.0.1" = { @@ -1805,13 +1814,13 @@ let sha1 = "22817534f24bfa4950c34d532d48ecbc621b8c14"; }; }; - "clipboard-2.0.4" = { + "clipboard-2.0.6" = { name = "clipboard"; packageName = "clipboard"; - version = "2.0.4"; - src = fetchurl { - url = "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz"; - sha512 = "Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ=="; + version = "2.0.6"; + src = fetchurl { + url = "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz"; + sha512 = "g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg=="; }; }; "cliui-2.1.0" = { @@ -2066,13 +2075,13 @@ let sha1 = "c20b96d8c617748aaf1c16021760cd27fcb8cb75"; }; }; - "convert-source-map-1.6.0" = { + "convert-source-map-1.7.0" = { name = "convert-source-map"; packageName = "convert-source-map"; - version = "1.6.0"; - src = fetchurl { - url = "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz"; - sha512 = "eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A=="; + version = "1.7.0"; + src = fetchurl { + url = "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz"; + sha512 = "4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA=="; }; }; "copy-concurrently-1.0.5" = { @@ -2102,13 +2111,13 @@ let sha512 = "Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA=="; }; }; - "core-js-2.6.10" = { + "core-js-2.6.11" = { name = "core-js"; packageName = "core-js"; - version = "2.6.10"; - src = fetchurl { - url = "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz"; - sha512 = "I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA=="; + version = "2.6.11"; + src = fetchurl { + url = "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz"; + sha512 = "5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="; }; }; "core-util-is-1.0.2" = { @@ -2201,13 +2210,13 @@ let sha1 = "2b3a110539c5355f1cd8d314623e870b121ec858"; }; }; - "css-selector-tokenizer-0.7.1" = { + "css-selector-tokenizer-0.7.2" = { name = "css-selector-tokenizer"; packageName = "css-selector-tokenizer"; - version = "0.7.1"; - src = fetchurl { - url = "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz"; - sha512 = "xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA=="; + version = "0.7.2"; + src = fetchurl { + url = "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz"; + sha512 = "yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw=="; }; }; "css-what-2.1.3" = { @@ -2219,13 +2228,13 @@ let sha512 = "a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="; }; }; - "cssesc-0.1.0" = { + "cssesc-3.0.0" = { name = "cssesc"; packageName = "cssesc"; - version = "0.1.0"; - src = fetchurl { - url = "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz"; - sha1 = "c814903e45623371a0477b40109aaafbeeaddbb4"; + version = "3.0.0"; + src = fetchurl { + url = "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"; + sha512 = "/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="; }; }; "cssnano-3.10.0" = { @@ -2390,13 +2399,13 @@ let sha512 = "IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="; }; }; - "des.js-1.0.0" = { + "des.js-1.0.1" = { name = "des.js"; packageName = "des.js"; - version = "1.0.0"; - src = fetchurl { - url = "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz"; - sha1 = "c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"; + version = "1.0.1"; + src = fetchurl { + url = "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz"; + sha512 = "Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA=="; }; }; "detect-file-1.0.0" = { @@ -2444,13 +2453,13 @@ let sha512 = "gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA=="; }; }; - "dom-serializer-0.2.1" = { + "dom-serializer-0.2.2" = { name = "dom-serializer"; packageName = "dom-serializer"; - version = "0.2.1"; - src = fetchurl { - url = "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz"; - sha512 = "sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q=="; + version = "0.2.2"; + src = fetchurl { + url = "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz"; + sha512 = "2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g=="; }; }; "dom5-2.3.0" = { @@ -2507,13 +2516,13 @@ let sha1 = "dcd8488a26f563d61079e48c9f7b7e32373682cf"; }; }; - "dropzone-5.5.1" = { + "dropzone-5.7.0" = { name = "dropzone"; packageName = "dropzone"; - version = "5.5.1"; - src = fetchurl { - url = "https://registry.npmjs.org/dropzone/-/dropzone-5.5.1.tgz"; - sha512 = "3VduRWLxx9hbVr42QieQN25mx/I61/mRdUSuxAmDGdDqZIN8qtP7tcKMa3KfpJjuGjOJGYYUzzeq6eGDnkzesA=="; + version = "5.7.0"; + src = fetchurl { + url = "https://registry.npmjs.org/dropzone/-/dropzone-5.7.0.tgz"; + sha512 = "kOltiZXH5cO/72I22JjE+w6BoT6uaVLfWdFMsi1PMKFkU6BZWpqRwjnsRm0o6ANGTBuZar5Piu7m/CbKqRPiYg=="; }; }; "duplexer-0.1.1" = { @@ -2543,22 +2552,22 @@ let sha1 = "3a83a904e54353287874c564b7549386849a98c9"; }; }; - "electron-to-chromium-1.3.302" = { + "electron-to-chromium-1.3.412" = { name = "electron-to-chromium"; packageName = "electron-to-chromium"; - version = "1.3.302"; - src = fetchurl { - url = "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.302.tgz"; - sha512 = "1qConyiVEbj4xZRBXqtGR003+9tV0rJF0PS6aeO0Ln/UL637js9hdwweCl07meh/kJoI2N4W8q3R3g3F5z46ww=="; - }; - }; - "elliptic-6.5.1" = { + version = "1.3.412"; + src = fetchurl { + url = "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.412.tgz"; + sha512 = "4bVdSeJScR8fT7ERveLWbxemY5uXEHVseqMRyORosiKcTUSGtVwBkV8uLjXCqoFLeImA57Z9hbz3TOid01U4Hw=="; + }; + }; + "elliptic-6.5.2" = { name = "elliptic"; packageName = "elliptic"; - version = "6.5.1"; - src = fetchurl { - url = "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz"; - sha512 = "xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg=="; + version = "6.5.2"; + src = fetchurl { + url = "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz"; + sha512 = "f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw=="; }; }; "emojis-list-2.1.0" = { @@ -2570,6 +2579,15 @@ let sha1 = "4daa4d9db00f9819880c79fa457ae5b09a1fd389"; }; }; + "emojis-list-3.0.0" = { + name = "emojis-list"; + packageName = "emojis-list"; + version = "3.0.0"; + src = fetchurl { + url = "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz"; + sha512 = "/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="; + }; + }; "end-of-stream-1.4.4" = { name = "end-of-stream"; packageName = "end-of-stream"; @@ -2624,22 +2642,22 @@ let sha512 = "MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg=="; }; }; - "es-abstract-1.16.0" = { + "es-abstract-1.17.5" = { name = "es-abstract"; packageName = "es-abstract"; - version = "1.16.0"; - src = fetchurl { - url = "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz"; - sha512 = "xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg=="; - }; - }; - "es-to-primitive-1.2.0" = { + version = "1.17.5"; + src = fetchurl { + url = "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz"; + sha512 = "BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg=="; + }; + }; + "es-to-primitive-1.2.1" = { name = "es-to-primitive"; packageName = "es-to-primitive"; - version = "1.2.0"; - src = fetchurl { - url = "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz"; - sha512 = "qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg=="; + version = "1.2.1"; + src = fetchurl { + url = "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz"; + sha512 = "QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA=="; }; }; "es6-templates-0.2.3" = { @@ -2741,13 +2759,13 @@ let sha1 = "8f61b75cde012b2e9eb284d4545583b5643b61ab"; }; }; - "events-3.0.0" = { + "events-3.1.0" = { name = "events"; packageName = "events"; - version = "3.0.0"; - src = fetchurl { - url = "https://registry.npmjs.org/events/-/events-3.0.0.tgz"; - sha512 = "Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA=="; + version = "3.1.0"; + src = fetchurl { + url = "https://registry.npmjs.org/events/-/events-3.1.0.tgz"; + sha512 = "Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg=="; }; }; "evp_bytestokey-1.0.3" = { @@ -2849,22 +2867,22 @@ let sha1 = "96918440e3041a7a414f8c52e3c574eb3c3e1e05"; }; }; - "fast-deep-equal-2.0.1" = { + "fast-deep-equal-3.1.1" = { name = "fast-deep-equal"; packageName = "fast-deep-equal"; - version = "2.0.1"; - src = fetchurl { - url = "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz"; - sha1 = "7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"; - }; - }; - "fast-json-stable-stringify-2.0.0" = { + version = "3.1.1"; + src = fetchurl { + url = "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz"; + sha512 = "8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="; + }; + }; + "fast-json-stable-stringify-2.1.0" = { name = "fast-json-stable-stringify"; packageName = "fast-json-stable-stringify"; - version = "2.0.0"; - src = fetchurl { - url = "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz"; - sha1 = "d5142c0caee6b1189f87d3a76111064f86c8bbf2"; + version = "2.1.0"; + src = fetchurl { + url = "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"; + sha512 = "lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="; }; }; "fastparse-1.1.2" = { @@ -2912,6 +2930,15 @@ let sha1 = "a5e7a8ffbfa493b43b923bbd4ca89a53b63b612b"; }; }; + "file-uri-to-path-1.0.0" = { + name = "file-uri-to-path"; + packageName = "file-uri-to-path"; + version = "1.0.0"; + src = fetchurl { + url = "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz"; + sha512 = "0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="; + }; + }; "fill-range-4.0.0" = { name = "fill-range"; packageName = "fill-range"; @@ -3074,13 +3101,13 @@ let sha1 = "1504ad2523158caa40db4a2787cb01411994ea4f"; }; }; - "fsevents-1.2.9" = { + "fsevents-1.2.12" = { name = "fsevents"; packageName = "fsevents"; - version = "1.2.9"; - src = fetchurl { - url = "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz"; - sha512 = "oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw=="; + version = "1.2.12"; + src = fetchurl { + url = "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz"; + sha512 = "Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q=="; }; }; "function-bind-1.1.1" = { @@ -3164,13 +3191,13 @@ let sha1 = "4a973f635b9190f715d10987d5c00fd2815ebe3d"; }; }; - "glob-7.1.5" = { + "glob-7.1.6" = { name = "glob"; packageName = "glob"; - version = "7.1.5"; - src = fetchurl { - url = "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz"; - sha512 = "J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ=="; + version = "7.1.6"; + src = fetchurl { + url = "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"; + sha512 = "LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA=="; }; }; "glob-parent-3.1.0" = { @@ -3452,13 +3479,13 @@ let sha1 = "b5d454dc2199ae225699f3467e5a07f3b955bafd"; }; }; - "has-symbols-1.0.0" = { + "has-symbols-1.0.1" = { name = "has-symbols"; packageName = "has-symbols"; - version = "1.0.0"; - src = fetchurl { - url = "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz"; - sha1 = "ba1a8f1af2a0fc39650f5c850367704122063b44"; + version = "1.0.1"; + src = fetchurl { + url = "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz"; + sha512 = "PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="; }; }; "has-value-0.3.1" = { @@ -3884,13 +3911,13 @@ let sha512 = "NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="; }; }; - "is-callable-1.1.4" = { + "is-callable-1.1.5" = { name = "is-callable"; packageName = "is-callable"; - version = "1.1.4"; - src = fetchurl { - url = "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz"; - sha512 = "r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="; + version = "1.1.5"; + src = fetchurl { + url = "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz"; + sha512 = "ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="; }; }; "is-data-descriptor-0.1.4" = { @@ -3911,13 +3938,13 @@ let sha512 = "jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ=="; }; }; - "is-date-object-1.0.1" = { + "is-date-object-1.0.2" = { name = "is-date-object"; packageName = "is-date-object"; - version = "1.0.1"; - src = fetchurl { - url = "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz"; - sha1 = "9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"; + version = "1.0.2"; + src = fetchurl { + url = "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz"; + sha512 = "USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="; }; }; "is-descriptor-0.1.6" = { @@ -3965,13 +3992,13 @@ let sha1 = "a88c02535791f02ed37c76a1b9ea9773c833f8c2"; }; }; - "is-finite-1.0.2" = { + "is-finite-1.1.0" = { name = "is-finite"; packageName = "is-finite"; - version = "1.0.2"; - src = fetchurl { - url = "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz"; - sha1 = "cc6677695602be550ef11e8b4aa6305342b6d0aa"; + version = "1.1.0"; + src = fetchurl { + url = "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz"; + sha512 = "cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="; }; }; "is-fullwidth-code-point-1.0.0" = { @@ -4037,13 +4064,13 @@ let sha512 = "h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og=="; }; }; - "is-regex-1.0.4" = { + "is-regex-1.0.5" = { name = "is-regex"; packageName = "is-regex"; - version = "1.0.4"; - src = fetchurl { - url = "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz"; - sha1 = "5517489b547091b0930e095654ced25ee97e9491"; + version = "1.0.5"; + src = fetchurl { + url = "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz"; + sha512 = "vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ=="; }; }; "is-relative-1.0.0" = { @@ -4073,13 +4100,13 @@ let sha1 = "cf61090da0d9efbcab8722deba6f032208dbb0e9"; }; }; - "is-symbol-1.0.2" = { + "is-symbol-1.0.3" = { name = "is-symbol"; packageName = "is-symbol"; - version = "1.0.2"; - src = fetchurl { - url = "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz"; - sha512 = "HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw=="; + version = "1.0.3"; + src = fetchurl { + url = "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz"; + sha512 = "OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ=="; }; }; "is-typedarray-1.0.0" = { @@ -4172,13 +4199,13 @@ let sha1 = "dd8b74278b27102d29df63eae28308a8cfa1b583"; }; }; - "js-base64-2.5.1" = { + "js-base64-2.5.2" = { name = "js-base64"; packageName = "js-base64"; - version = "2.5.1"; - src = fetchurl { - url = "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz"; - sha512 = "M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw=="; + version = "2.5.2"; + src = fetchurl { + url = "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz"; + sha512 = "Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="; }; }; "js-tokens-3.0.2" = { @@ -4235,13 +4262,13 @@ let sha1 = "46c3fec8c1892b12b0833db9bc7622176dbab34b"; }; }; - "jshint-2.10.2" = { + "jshint-2.11.0" = { name = "jshint"; packageName = "jshint"; - version = "2.10.2"; - src = fetchurl { - url = "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz"; - sha512 = "e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA=="; + version = "2.11.0"; + src = fetchurl { + url = "https://registry.npmjs.org/jshint/-/jshint-2.11.0.tgz"; + sha512 = "ooaD/hrBPhu35xXW4gn+o3SOuzht73gdBuffgJzrZBJZPGgGiiTvJEgTyxFvBO2nz0+X1G6etF8SzUODTlLY6Q=="; }; }; "jshint-2.9.7" = { @@ -4361,13 +4388,13 @@ let sha512 = "NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="; }; }; - "kind-of-6.0.2" = { + "kind-of-6.0.3" = { name = "kind-of"; packageName = "kind-of"; - version = "6.0.2"; - src = fetchurl { - url = "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz"; - sha512 = "s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="; + version = "6.0.3"; + src = fetchurl { + url = "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"; + sha512 = "dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="; }; }; "lazy-cache-1.0.4" = { @@ -4424,13 +4451,13 @@ let sha1 = "f86e6374d43205a6e6c60e9196f17c0299bfb348"; }; }; - "loader-utils-1.2.3" = { + "loader-utils-1.4.0" = { name = "loader-utils"; packageName = "loader-utils"; - version = "1.2.3"; - src = fetchurl { - url = "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz"; - sha512 = "fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA=="; + version = "1.4.0"; + src = fetchurl { + url = "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz"; + sha512 = "qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA=="; }; }; "locate-path-2.0.0" = { @@ -4622,13 +4649,13 @@ let sha1 = "180f1f9ebef8b0e638e4166ad52db879beb2ffc5"; }; }; - "math-expression-evaluator-1.2.17" = { + "math-expression-evaluator-1.2.22" = { name = "math-expression-evaluator"; packageName = "math-expression-evaluator"; - version = "1.2.17"; - src = fetchurl { - url = "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz"; - sha1 = "de819fdbcd84dccd8fae59c6aeb79615b9d266ac"; + version = "1.2.22"; + src = fetchurl { + url = "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz"; + sha512 = "L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ=="; }; }; "maxmin-2.1.0" = { @@ -4703,22 +4730,22 @@ let sha512 = "x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="; }; }; - "mime-db-1.40.0" = { + "mime-db-1.43.0" = { name = "mime-db"; packageName = "mime-db"; - version = "1.40.0"; - src = fetchurl { - url = "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz"; - sha512 = "jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="; - }; - }; - "mime-types-2.1.24" = { + version = "1.43.0"; + src = fetchurl { + url = "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz"; + sha512 = "+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="; + }; + }; + "mime-types-2.1.26" = { name = "mime-types"; packageName = "mime-types"; - version = "2.1.24"; - src = fetchurl { - url = "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz"; - sha512 = "WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ=="; + version = "2.1.26"; + src = fetchurl { + url = "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz"; + sha512 = "01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ=="; }; }; "mimic-fn-2.1.0" = { @@ -4775,22 +4802,13 @@ let sha512 = "yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA=="; }; }; - "minimist-0.0.8" = { + "minimist-1.2.5" = { name = "minimist"; packageName = "minimist"; - version = "0.0.8"; - src = fetchurl { - url = "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"; - sha1 = "857fcabfc3397d2625b8228262e86aa7a011b05d"; - }; - }; - "minimist-1.2.0" = { - name = "minimist"; - packageName = "minimist"; - version = "1.2.0"; - src = fetchurl { - url = "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"; - sha1 = "a35008b20f41383eec1fb914f4cd5df79a264284"; + version = "1.2.5"; + src = fetchurl { + url = "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz"; + sha512 = "FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="; }; }; "mississippi-2.0.0" = { @@ -4811,13 +4829,13 @@ let sha512 = "WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA=="; }; }; - "mkdirp-0.5.1" = { + "mkdirp-0.5.5" = { name = "mkdirp"; packageName = "mkdirp"; - version = "0.5.1"; - src = fetchurl { - url = "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"; - sha1 = "30057438eac6cf7f8c4767f38648d6697d75c903"; + version = "0.5.5"; + src = fetchurl { + url = "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz"; + sha512 = "NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ=="; }; }; "moment-2.24.0" = { @@ -4829,13 +4847,13 @@ let sha512 = "bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="; }; }; - "mousetrap-1.6.3" = { + "mousetrap-1.6.5" = { name = "mousetrap"; packageName = "mousetrap"; - version = "1.6.3"; - src = fetchurl { - url = "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.3.tgz"; - sha512 = "bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA=="; + version = "1.6.5"; + src = fetchurl { + url = "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz"; + sha512 = "QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA=="; }; }; "move-concurrently-1.0.1" = { @@ -4928,13 +4946,13 @@ let sha1 = "ca7416f20a5e3f9c3b86180f96295fa3d0b52e0d"; }; }; - "nopt-4.0.1" = { + "nopt-4.0.3" = { name = "nopt"; packageName = "nopt"; - version = "4.0.1"; - src = fetchurl { - url = "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz"; - sha1 = "d0d4685afd5415193c8c7505602d0d17cd64474d"; + version = "4.0.3"; + src = fetchurl { + url = "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz"; + sha512 = "CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg=="; }; }; "noptify-0.0.3" = { @@ -5045,13 +5063,13 @@ let sha1 = "7e7d858b781bd7c991a41ba975ed3812754e998c"; }; }; - "object-inspect-1.6.0" = { + "object-inspect-1.7.0" = { name = "object-inspect"; packageName = "object-inspect"; - version = "1.6.0"; - src = fetchurl { - url = "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz"; - sha512 = "GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ=="; + version = "1.7.0"; + src = fetchurl { + url = "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz"; + sha512 = "a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="; }; }; "object-keys-1.1.1" = { @@ -5072,6 +5090,15 @@ let sha1 = "f79c4493af0c5377b59fe39d395e41042dd045bb"; }; }; + "object.assign-4.1.0" = { + name = "object.assign"; + packageName = "object.assign"; + version = "4.1.0"; + src = fetchurl { + url = "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz"; + sha512 = "exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w=="; + }; + }; "object.defaults-1.1.0" = { name = "object.defaults"; packageName = "object.defaults"; @@ -5081,13 +5108,13 @@ let sha1 = "3a7f868334b407dea06da16d88d5cd29e435fecf"; }; }; - "object.getownpropertydescriptors-2.0.3" = { + "object.getownpropertydescriptors-2.1.0" = { name = "object.getownpropertydescriptors"; packageName = "object.getownpropertydescriptors"; - version = "2.0.3"; - src = fetchurl { - url = "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz"; - sha1 = "8758c846f5b407adab0f236e0986f14b051caa16"; + version = "2.1.0"; + src = fetchurl { + url = "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz"; + sha512 = "Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg=="; }; }; "object.map-1.0.1" = { @@ -5198,13 +5225,13 @@ let sha512 = "vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q=="; }; }; - "p-limit-2.2.1" = { + "p-limit-2.3.0" = { name = "p-limit"; packageName = "p-limit"; - version = "2.2.1"; - src = fetchurl { - url = "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz"; - sha512 = "85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg=="; + version = "2.3.0"; + src = fetchurl { + url = "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"; + sha512 = "//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="; }; }; "p-locate-2.0.0" = { @@ -5243,13 +5270,13 @@ let sha512 = "R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="; }; }; - "pako-1.0.10" = { + "pako-1.0.11" = { name = "pako"; packageName = "pako"; - version = "1.0.10"; - src = fetchurl { - url = "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz"; - sha512 = "0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw=="; + version = "1.0.11"; + src = fetchurl { + url = "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz"; + sha512 = "4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="; }; }; "parallel-transform-1.2.0" = { @@ -6035,13 +6062,13 @@ let sha1 = "7cf4c54ef648e3813084c636dd2079e166c081d9"; }; }; - "readable-stream-2.3.6" = { + "readable-stream-2.3.7" = { name = "readable-stream"; packageName = "readable-stream"; - version = "2.3.6"; - src = fetchurl { - url = "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz"; - sha512 = "tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw=="; + version = "2.3.7"; + src = fetchurl { + url = "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"; + sha512 = "Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw=="; }; }; "readdirp-2.2.1" = { @@ -6098,6 +6125,15 @@ let sha512 = "1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg=="; }; }; + "regenerate-unicode-properties-8.2.0" = { + name = "regenerate-unicode-properties"; + packageName = "regenerate-unicode-properties"; + version = "8.2.0"; + src = fetchurl { + url = "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz"; + sha512 = "F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA=="; + }; + }; "regenerator-runtime-0.11.1" = { name = "regenerator-runtime"; packageName = "regenerator-runtime"; @@ -6125,15 +6161,6 @@ let sha512 = "J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A=="; }; }; - "regexpu-core-1.0.0" = { - name = "regexpu-core"; - packageName = "regexpu-core"; - version = "1.0.0"; - src = fetchurl { - url = "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz"; - sha1 = "86a763f58ee4d7c2f6b102e4764050de7ed90c6b"; - }; - }; "regexpu-core-2.0.0" = { name = "regexpu-core"; packageName = "regexpu-core"; @@ -6143,6 +6170,15 @@ let sha1 = "49d038837b8dcf8bfa5b9a42139938e6ea2ae240"; }; }; + "regexpu-core-4.7.0" = { + name = "regexpu-core"; + packageName = "regexpu-core"; + version = "4.7.0"; + src = fetchurl { + url = "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz"; + sha512 = "TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ=="; + }; + }; "regjsgen-0.2.0" = { name = "regjsgen"; packageName = "regjsgen"; @@ -6152,6 +6188,15 @@ let sha1 = "6c016adeac554f75823fe37ac05b92d5a4edb1f7"; }; }; + "regjsgen-0.5.1" = { + name = "regjsgen"; + packageName = "regjsgen"; + version = "0.5.1"; + src = fetchurl { + url = "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz"; + sha512 = "5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg=="; + }; + }; "regjsparser-0.1.5" = { name = "regjsparser"; packageName = "regjsparser"; @@ -6161,6 +6206,15 @@ let sha1 = "7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"; }; }; + "regjsparser-0.6.4" = { + name = "regjsparser"; + packageName = "regjsparser"; + version = "0.6.4"; + src = fetchurl { + url = "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz"; + sha512 = "64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw=="; + }; + }; "relateurl-0.2.7" = { name = "relateurl"; packageName = "relateurl"; @@ -6242,13 +6296,13 @@ let sha1 = "97f717b69d48784f5f526a6c5aa8ffdda055a4d1"; }; }; - "resolve-1.12.0" = { + "resolve-1.16.0" = { name = "resolve"; packageName = "resolve"; - version = "1.12.0"; - src = fetchurl { - url = "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz"; - sha512 = "B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w=="; + version = "1.16.0"; + src = fetchurl { + url = "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz"; + sha512 = "LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA=="; }; }; "resolve-cwd-2.0.0" = { @@ -6494,13 +6548,13 @@ let sha1 = "3ff21f198cad2175f9f3b781853fd94d0d19b590"; }; }; - "signal-exit-3.0.2" = { + "signal-exit-3.0.3" = { name = "signal-exit"; packageName = "signal-exit"; - version = "3.0.2"; - src = fetchurl { - url = "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz"; - sha1 = "b5fdc08f1287ea1178628e415e25132b73646c6d"; + version = "3.0.3"; + src = fetchurl { + url = "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz"; + sha512 = "VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="; }; }; "slash-1.0.0" = { @@ -6611,13 +6665,13 @@ let sha512 = "UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="; }; }; - "source-map-resolve-0.5.2" = { + "source-map-resolve-0.5.3" = { name = "source-map-resolve"; packageName = "source-map-resolve"; - version = "0.5.2"; - src = fetchurl { - url = "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz"; - sha512 = "MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA=="; + version = "0.5.3"; + src = fetchurl { + url = "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz"; + sha512 = "Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw=="; }; }; "source-map-support-0.4.18" = { @@ -6719,13 +6773,13 @@ let sha512 = "+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw=="; }; }; - "stream-shift-1.0.0" = { + "stream-shift-1.0.1" = { name = "stream-shift"; packageName = "stream-shift"; - version = "1.0.0"; - src = fetchurl { - url = "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz"; - sha1 = "d5c752825e5367e786f78e18e445ea223a155952"; + version = "1.0.1"; + src = fetchurl { + url = "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz"; + sha512 = "AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="; }; }; "strict-uri-encode-1.1.0" = { @@ -6755,22 +6809,40 @@ let sha512 = "nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="; }; }; - "string.prototype.trimleft-2.1.0" = { + "string.prototype.trimend-1.0.1" = { + name = "string.prototype.trimend"; + packageName = "string.prototype.trimend"; + version = "1.0.1"; + src = fetchurl { + url = "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz"; + sha512 = "LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g=="; + }; + }; + "string.prototype.trimleft-2.1.2" = { name = "string.prototype.trimleft"; packageName = "string.prototype.trimleft"; - version = "2.1.0"; - src = fetchurl { - url = "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz"; - sha512 = "FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw=="; - }; - }; - "string.prototype.trimright-2.1.0" = { + version = "2.1.2"; + src = fetchurl { + url = "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz"; + sha512 = "gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw=="; + }; + }; + "string.prototype.trimright-2.1.2" = { name = "string.prototype.trimright"; packageName = "string.prototype.trimright"; - version = "2.1.0"; - src = fetchurl { - url = "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz"; - sha512 = "fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg=="; + version = "2.1.2"; + src = fetchurl { + url = "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz"; + sha512 = "ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg=="; + }; + }; + "string.prototype.trimstart-1.0.1" = { + name = "string.prototype.trimstart"; + packageName = "string.prototype.trimstart"; + version = "1.0.1"; + src = fetchurl { + url = "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz"; + sha512 = "XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw=="; }; }; "string_decoder-0.10.31" = { @@ -6908,6 +6980,15 @@ let sha1 = "9f5772413952135c6fefbf40afe6a4faa88b4bb5"; }; }; + "sweetalert2-9.10.12" = { + name = "sweetalert2"; + packageName = "sweetalert2"; + version = "9.10.12"; + src = fetchurl { + url = "https://registry.npmjs.org/sweetalert2/-/sweetalert2-9.10.12.tgz"; + sha512 = "RnarmbDGTPmwecJbaVdq5LvlzbVReIOtPk0huPnXOE19G00xMxGcTY0wjt9AjwsexUnLivLXc3b6nD6+D6NlGg=="; + }; + }; "tapable-0.2.9" = { name = "tapable"; packageName = "tapable"; @@ -7052,13 +7133,13 @@ let sha1 = "30c6203e1e66b841a88701ed8858f1725d94b026"; }; }; - "tslib-1.10.0" = { + "tslib-1.11.1" = { name = "tslib"; packageName = "tslib"; - version = "1.10.0"; - src = fetchurl { - url = "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz"; - sha512 = "qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="; + version = "1.11.1"; + src = fetchurl { + url = "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz"; + sha512 = "aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="; }; }; "tty-browserify-0.0.0" = { @@ -7124,13 +7205,13 @@ let sha512 = "Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw=="; }; }; - "uglify-js-3.6.7" = { + "uglify-js-3.9.1" = { name = "uglify-js"; packageName = "uglify-js"; - version = "3.6.7"; - src = fetchurl { - url = "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.7.tgz"; - sha512 = "4sXQDzmdnoXiO+xvmTzQsfIiwrjUCSA95rSP4SEd8tDb51W2TiDOlL76Hl+Kw0Ie42PSItCW8/t6pBNCF2R48A=="; + version = "3.9.1"; + src = fetchurl { + url = "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz"; + sha512 = "JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA=="; }; }; "uglify-to-browserify-1.0.2" = { @@ -7196,6 +7277,42 @@ let sha1 = "8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b"; }; }; + "unicode-canonical-property-names-ecmascript-1.0.4" = { + name = "unicode-canonical-property-names-ecmascript"; + packageName = "unicode-canonical-property-names-ecmascript"; + version = "1.0.4"; + src = fetchurl { + url = "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz"; + sha512 = "jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ=="; + }; + }; + "unicode-match-property-ecmascript-1.0.4" = { + name = "unicode-match-property-ecmascript"; + packageName = "unicode-match-property-ecmascript"; + version = "1.0.4"; + src = fetchurl { + url = "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz"; + sha512 = "L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg=="; + }; + }; + "unicode-match-property-value-ecmascript-1.2.0" = { + name = "unicode-match-property-value-ecmascript"; + packageName = "unicode-match-property-value-ecmascript"; + version = "1.2.0"; + src = fetchurl { + url = "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz"; + sha512 = "wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ=="; + }; + }; + "unicode-property-aliases-ecmascript-1.1.0" = { + name = "unicode-property-aliases-ecmascript"; + packageName = "unicode-property-aliases-ecmascript"; + version = "1.1.0"; + src = fetchurl { + url = "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz"; + sha512 = "PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg=="; + }; + }; "union-value-1.0.1" = { name = "union-value"; packageName = "union-value"; @@ -7358,13 +7475,13 @@ let sha1 = "8a16a05d445657a3aea5eecc5b12a4fa5379772c"; }; }; - "uuid-3.3.3" = { + "uuid-3.4.0" = { name = "uuid"; packageName = "uuid"; - version = "3.3.3"; - src = fetchurl { - url = "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz"; - sha512 = "pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="; + version = "3.4.0"; + src = fetchurl { + url = "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"; + sha512 = "HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="; }; }; "v8-compile-cache-2.1.0" = { @@ -7385,13 +7502,13 @@ let sha512 = "amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w=="; }; }; - "vendors-1.0.3" = { + "vendors-1.0.4" = { name = "vendors"; packageName = "vendors"; - version = "1.0.3"; - src = fetchurl { - url = "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz"; - sha512 = "fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw=="; + version = "1.0.4"; + src = fetchurl { + url = "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz"; + sha512 = "/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w=="; }; }; "verror-1.10.0" = { @@ -7403,22 +7520,22 @@ let sha1 = "3a105ca17053af55d6e270c1f8288682e18da400"; }; }; - "vm-browserify-1.1.0" = { + "vm-browserify-1.1.2" = { name = "vm-browserify"; packageName = "vm-browserify"; - version = "1.1.0"; - src = fetchurl { - url = "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz"; - sha512 = "iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw=="; - }; - }; - "watchpack-1.6.0" = { + version = "1.1.2"; + src = fetchurl { + url = "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz"; + sha512 = "2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="; + }; + }; + "watchpack-1.6.1" = { name = "watchpack"; packageName = "watchpack"; - version = "1.6.0"; - src = fetchurl { - url = "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz"; - sha512 = "i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA=="; + version = "1.6.1"; + src = fetchurl { + url = "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz"; + sha512 = "+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA=="; }; }; "waypoints-4.0.1" = { @@ -7622,27 +7739,27 @@ let sources."@polymer/iron-a11y-keys-3.0.1" sources."@polymer/iron-a11y-keys-behavior-3.0.1" sources."@polymer/iron-ajax-3.0.1" - sources."@polymer/iron-autogrow-textarea-3.0.1" + sources."@polymer/iron-autogrow-textarea-3.0.3" sources."@polymer/iron-behaviors-3.0.1" sources."@polymer/iron-checked-element-behavior-3.0.1" - sources."@polymer/iron-fit-behavior-3.0.1" + sources."@polymer/iron-fit-behavior-3.0.2" sources."@polymer/iron-flex-layout-3.0.1" sources."@polymer/iron-form-element-behavior-3.0.1" sources."@polymer/iron-meta-3.0.1" - sources."@polymer/iron-overlay-behavior-3.0.2" + sources."@polymer/iron-overlay-behavior-3.0.3" sources."@polymer/iron-resizable-behavior-3.0.1" sources."@polymer/iron-validatable-behavior-3.0.1" sources."@polymer/paper-behaviors-3.0.1" sources."@polymer/paper-button-3.0.1" - sources."@polymer/paper-ripple-3.0.1" + sources."@polymer/paper-ripple-3.0.2" sources."@polymer/paper-spinner-3.0.2" sources."@polymer/paper-styles-3.0.1" sources."@polymer/paper-toast-3.0.1" sources."@polymer/paper-toggle-button-3.0.1" sources."@polymer/paper-tooltip-3.0.1" - sources."@polymer/polymer-3.3.0" + sources."@polymer/polymer-3.3.1" sources."@types/clone-0.1.30" - sources."@types/node-6.14.9" + sources."@types/node-6.14.10" sources."@types/parse5-2.2.34" sources."@webassemblyjs/ast-1.7.10" sources."@webassemblyjs/floating-point-hex-parser-1.7.10" @@ -7662,12 +7779,12 @@ let sources."@webassemblyjs/wasm-parser-1.7.10" sources."@webassemblyjs/wast-parser-1.7.10" sources."@webassemblyjs/wast-printer-1.7.10" - sources."@webcomponents/shadycss-1.9.2" - sources."@webcomponents/webcomponentsjs-2.3.0" + sources."@webcomponents/shadycss-1.9.6" + sources."@webcomponents/webcomponentsjs-2.4.3" sources."@xtuc/ieee754-1.2.0" sources."@xtuc/long-4.2.1" sources."abbrev-1.1.1" - sources."acorn-5.7.3" + sources."acorn-5.7.4" sources."acorn-dynamic-import-3.0.0" (sources."acorn-jsx-3.0.1" // { dependencies = [ @@ -7727,7 +7844,7 @@ let ]; }) sources."aws-sign2-0.6.0" - sources."aws4-1.8.0" + sources."aws4-1.9.1" (sources."babel-code-frame-6.26.0" // { dependencies = [ sources."ansi-regex-2.1.1" @@ -7841,7 +7958,8 @@ let sources."bcrypt-pbkdf-1.0.2" sources."big.js-5.2.2" sources."binary-extensions-1.13.1" - sources."bluebird-3.7.1" + sources."bindings-1.5.0" + sources."bluebird-3.7.2" sources."bn.js-4.11.8" sources."boolbase-1.0.0" sources."boom-2.10.1" @@ -7859,13 +7977,13 @@ let sources."browserify-sign-4.0.4" sources."browserify-zlib-0.2.0" sources."browserslist-3.2.8" - sources."buffer-4.9.1" + sources."buffer-4.9.2" sources."buffer-from-1.1.1" sources."buffer-xor-1.0.3" sources."builtin-status-codes-3.0.0" (sources."cacache-10.0.4" // { dependencies = [ - sources."glob-7.1.5" + sources."glob-7.1.6" sources."graceful-fs-4.2.3" sources."lru-cache-4.1.5" sources."minimatch-3.0.4" @@ -7880,8 +7998,8 @@ let sources."browserslist-1.7.7" ]; }) - sources."caniuse-db-1.0.30001006" - sources."caniuse-lite-1.0.30001006" + sources."caniuse-db-1.0.30001042" + sources."caniuse-lite-1.0.30001042" sources."caseless-0.12.0" sources."center-align-0.1.3" sources."chalk-0.5.1" @@ -7890,7 +8008,7 @@ let sources."is-glob-4.0.1" ]; }) - sources."chownr-1.1.3" + sources."chownr-1.1.4" sources."chrome-trace-event-1.0.2" sources."cipher-base-1.0.4" (sources."clap-1.2.3" // { @@ -7920,18 +8038,18 @@ let sources."kind-of-5.1.0" ]; }) - (sources."clean-css-4.2.1" // { + (sources."clean-css-4.2.3" // { dependencies = [ sources."source-map-0.6.1" ]; }) (sources."cli-1.0.1" // { dependencies = [ - sources."glob-7.1.5" + sources."glob-7.1.6" sources."minimatch-3.0.4" ]; }) - sources."clipboard-2.0.4" + sources."clipboard-2.0.6" (sources."cliui-4.1.0" // { dependencies = [ sources."ansi-regex-3.0.0" @@ -7957,21 +8075,21 @@ let sources."concat-map-0.0.1" (sources."concat-stream-1.6.2" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; }) sources."console-browserify-1.1.0" sources."constants-browserify-1.0.0" - (sources."convert-source-map-1.6.0" // { + (sources."convert-source-map-1.7.0" // { dependencies = [ sources."safe-buffer-5.1.2" ]; }) (sources."copy-concurrently-1.0.5" // { dependencies = [ - sources."glob-7.1.5" + sources."glob-7.1.6" sources."minimatch-3.0.4" sources."rimraf-2.7.1" ]; @@ -7983,7 +8101,7 @@ let sources."minimatch-3.0.4" ]; }) - sources."core-js-2.6.10" + sources."core-js-2.6.11" sources."core-util-is-1.0.2" sources."create-ecdh-4.0.3" sources."create-hash-1.2.0" @@ -7998,13 +8116,16 @@ let sources."css-color-names-0.0.4" sources."css-loader-0.28.11" sources."css-select-1.2.0" - (sources."css-selector-tokenizer-0.7.1" // { - dependencies = [ - sources."regexpu-core-1.0.0" + (sources."css-selector-tokenizer-0.7.2" // { + dependencies = [ + sources."jsesc-0.5.0" + sources."regexpu-core-4.7.0" + sources."regjsgen-0.5.1" + sources."regjsparser-0.6.4" ]; }) sources."css-what-2.1.3" - sources."cssesc-0.1.0" + sources."cssesc-3.0.0" sources."cssnano-3.10.0" sources."csso-2.3.2" sources."cyclist-1.0.1" @@ -8024,13 +8145,13 @@ let sources."defined-1.0.0" sources."delayed-stream-1.0.0" sources."delegate-3.2.0" - sources."des.js-1.0.0" + sources."des.js-1.0.1" sources."detect-file-1.0.0" sources."detect-indent-4.0.0" sources."diffie-hellman-5.0.3" sources."dir-glob-2.2.2" sources."dom-converter-0.2.0" - (sources."dom-serializer-0.2.1" // { + (sources."dom-serializer-0.2.2" // { dependencies = [ sources."domelementtype-2.0.1" sources."entities-2.0.0" @@ -8046,33 +8167,33 @@ let sources."domelementtype-1.3.1" sources."domhandler-2.3.0" sources."domutils-1.5.1" - sources."dropzone-5.5.1" + sources."dropzone-5.7.0" sources."duplexer-0.1.1" (sources."duplexify-3.7.1" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; }) sources."ecc-jsbn-0.1.2" - sources."electron-to-chromium-1.3.302" - sources."elliptic-6.5.1" - sources."emojis-list-2.1.0" + sources."electron-to-chromium-1.3.412" + sources."elliptic-6.5.2" + sources."emojis-list-3.0.0" sources."end-of-stream-1.4.4" (sources."enhanced-resolve-4.1.1" // { dependencies = [ sources."graceful-fs-4.2.3" sources."memory-fs-0.5.0" - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; }) sources."entities-1.0.0" sources."errno-0.1.7" - sources."es-abstract-1.16.0" - sources."es-to-primitive-1.2.0" + sources."es-abstract-1.17.5" + sources."es-to-primitive-1.2.1" sources."es6-templates-0.2.3" sources."escape-string-regexp-1.0.5" sources."eslint-scope-4.0.3" @@ -8082,7 +8203,7 @@ let sources."estraverse-4.3.0" sources."esutils-2.0.3" sources."eventemitter2-0.4.14" - sources."events-3.0.0" + sources."events-3.1.0" sources."evp_bytestokey-1.0.3" sources."execa-1.0.0" sources."exit-0.1.2" @@ -8119,13 +8240,14 @@ let ]; }) sources."extsprintf-1.3.0" - sources."fast-deep-equal-2.0.1" - sources."fast-json-stable-stringify-2.0.0" + sources."fast-deep-equal-3.1.1" + sources."fast-json-stable-stringify-2.1.0" sources."fastparse-1.1.2" sources."favico.js-0.3.10" sources."faye-websocket-0.4.4" sources."figures-1.7.0" sources."file-sync-cmp-0.1.1" + sources."file-uri-to-path-1.0.0" (sources."fill-range-4.0.0" // { dependencies = [ sources."extend-shallow-2.0.1" @@ -8145,7 +8267,7 @@ let sources."flatten-1.0.3" (sources."flush-write-stream-1.1.1" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8157,7 +8279,7 @@ let sources."fragment-cache-0.2.1" (sources."from2-2.3.0" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8168,7 +8290,7 @@ let ]; }) sources."fs.realpath-1.0.0" - sources."fsevents-1.2.9" + sources."fsevents-1.2.12" sources."function-bind-1.1.1" sources."gaze-0.5.2" sources."get-caller-file-1.0.3" @@ -8200,7 +8322,7 @@ let sources."globals-9.18.0" (sources."globby-7.1.1" // { dependencies = [ - sources."glob-7.1.5" + sources."glob-7.1.6" sources."minimatch-3.0.4" ]; }) @@ -8214,7 +8336,7 @@ let sources."grunt-0.4.5" (sources."grunt-cli-1.3.2" // { dependencies = [ - sources."nopt-4.0.1" + sources."nopt-4.0.3" ]; }) (sources."grunt-contrib-concat-0.5.1" // { @@ -8289,7 +8411,7 @@ let sources."has-1.0.3" sources."has-ansi-0.1.0" sources."has-flag-3.0.0" - sources."has-symbols-1.0.0" + sources."has-symbols-1.0.1" sources."has-value-1.0.0" (sources."has-values-1.0.0" // { dependencies = [ @@ -8321,6 +8443,7 @@ let (sources."html-webpack-plugin-3.2.0" // { dependencies = [ sources."big.js-3.2.0" + sources."emojis-list-2.1.0" sources."json5-0.5.1" sources."loader-utils-0.2.17" sources."lodash-4.17.15" @@ -8348,7 +8471,7 @@ let dependencies = [ sources."find-up-3.0.0" sources."locate-path-3.0.0" - sources."p-limit-2.2.1" + sources."p-limit-2.3.0" sources."p-locate-3.0.0" sources."p-try-2.2.0" sources."pkg-dir-3.0.0" @@ -8368,13 +8491,13 @@ let sources."is-accessor-descriptor-1.0.0" sources."is-binary-path-1.0.1" sources."is-buffer-1.1.6" - sources."is-callable-1.1.4" + sources."is-callable-1.1.5" sources."is-data-descriptor-1.0.0" - sources."is-date-object-1.0.1" + sources."is-date-object-1.0.2" sources."is-descriptor-1.0.2" sources."is-extendable-0.1.1" sources."is-extglob-2.1.1" - sources."is-finite-1.0.2" + sources."is-finite-1.1.0" sources."is-fullwidth-code-point-1.0.0" sources."is-glob-3.1.0" (sources."is-number-3.0.0" // { @@ -8384,11 +8507,11 @@ let }) sources."is-plain-obj-1.1.0" sources."is-plain-object-2.0.4" - sources."is-regex-1.0.4" + sources."is-regex-1.0.5" sources."is-relative-1.0.0" sources."is-stream-1.1.0" sources."is-svg-2.1.0" - sources."is-symbol-1.0.2" + sources."is-symbol-1.0.3" sources."is-typedarray-1.0.0" sources."is-unc-path-1.0.0" sources."is-windows-1.0.2" @@ -8397,12 +8520,12 @@ let sources."isobject-3.0.1" sources."isstream-0.1.2" sources."jquery-1.11.3" - sources."js-base64-2.5.1" + sources."js-base64-2.5.2" sources."js-tokens-3.0.2" sources."js-yaml-2.0.5" sources."jsbn-0.1.1" sources."jsesc-1.3.0" - (sources."jshint-2.10.2" // { + (sources."jshint-2.11.0" // { dependencies = [ sources."lodash-4.17.15" sources."minimatch-3.0.4" @@ -8420,7 +8543,7 @@ let sources."assert-plus-1.0.0" ]; }) - sources."kind-of-6.0.2" + sources."kind-of-6.0.3" sources."lazy-cache-1.0.4" sources."lcid-2.0.0" (sources."less-2.7.3" // { @@ -8434,7 +8557,7 @@ let ]; }) sources."loader-runner-2.4.0" - sources."loader-utils-1.2.3" + sources."loader-utils-1.4.0" sources."locate-path-2.0.0" sources."lodash-0.9.2" sources."lodash.camelcase-4.3.0" @@ -8451,7 +8574,7 @@ let sources."map-cache-0.2.2" sources."map-visit-1.0.0" sources."mark.js-8.11.1" - sources."math-expression-evaluator-1.2.17" + sources."math-expression-evaluator-1.2.22" (sources."maxmin-2.1.0" // { dependencies = [ sources."ansi-regex-2.1.1" @@ -8466,7 +8589,7 @@ let sources."mem-4.3.0" (sources."memory-fs-0.4.1" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8474,29 +8597,25 @@ let sources."micromatch-3.1.10" sources."miller-rabin-4.0.1" sources."mime-1.6.0" - sources."mime-db-1.40.0" - sources."mime-types-2.1.24" + sources."mime-db-1.43.0" + sources."mime-types-2.1.26" sources."mimic-fn-2.1.0" sources."minimalistic-assert-1.0.1" sources."minimalistic-crypto-utils-1.0.1" sources."minimatch-0.2.14" - sources."minimist-1.2.0" + sources."minimist-1.2.5" sources."mississippi-2.0.0" (sources."mixin-deep-1.3.2" // { dependencies = [ sources."is-extendable-1.0.1" ]; }) - (sources."mkdirp-0.5.1" // { - dependencies = [ - sources."minimist-0.0.8" - ]; - }) + sources."mkdirp-0.5.5" sources."moment-2.24.0" - sources."mousetrap-1.6.3" + sources."mousetrap-1.6.5" (sources."move-concurrently-1.0.1" // { dependencies = [ - sources."glob-7.1.5" + sources."glob-7.1.6" sources."minimatch-3.0.4" sources."rimraf-2.7.1" ]; @@ -8509,7 +8628,7 @@ let sources."no-case-2.3.2" (sources."node-libs-browser-2.2.1" // { dependencies = [ - (sources."readable-stream-2.3.6" // { + (sources."readable-stream-2.3.7" // { dependencies = [ sources."string_decoder-1.1.1" ]; @@ -8550,11 +8669,12 @@ let sources."kind-of-3.2.2" ]; }) - sources."object-inspect-1.6.0" + sources."object-inspect-1.7.0" sources."object-keys-1.1.1" sources."object-visit-1.0.1" + sources."object.assign-4.1.0" sources."object.defaults-1.1.0" - sources."object.getownpropertydescriptors-2.0.3" + sources."object.getownpropertydescriptors-2.1.0" sources."object.map-1.0.1" sources."object.pick-1.3.0" sources."once-1.4.0" @@ -8569,10 +8689,10 @@ let sources."p-limit-1.3.0" sources."p-locate-2.0.0" sources."p-try-1.0.0" - sources."pako-1.0.10" + sources."pako-1.0.11" (sources."parallel-transform-1.2.0" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8729,7 +8849,7 @@ let (sources."readdirp-2.2.1" // { dependencies = [ sources."graceful-fs-4.2.3" - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8747,6 +8867,7 @@ let }) sources."reduce-function-call-1.0.3" sources."regenerate-1.4.0" + sources."regenerate-unicode-properties-8.2.0" sources."regenerator-runtime-0.11.1" sources."regenerator-transform-0.10.1" sources."regex-not-1.0.2" @@ -8771,7 +8892,7 @@ let sources."request-2.81.0" sources."require-directory-2.1.1" sources."require-main-filename-1.0.1" - sources."resolve-1.12.0" + sources."resolve-1.16.0" sources."resolve-cwd-2.0.0" sources."resolve-dir-1.0.1" sources."resolve-from-3.0.0" @@ -8787,7 +8908,7 @@ let sources."sax-1.2.4" (sources."schema-utils-0.4.7" // { dependencies = [ - sources."ajv-6.10.2" + sources."ajv-6.12.0" ]; }) sources."select-1.1.2" @@ -8805,7 +8926,7 @@ let sources."shebang-regex-1.0.0" sources."shelljs-0.3.0" sources."sigmund-1.0.1" - sources."signal-exit-3.0.2" + sources."signal-exit-3.0.3" sources."slash-1.0.0" (sources."snapdragon-0.8.2" // { dependencies = [ @@ -8839,7 +8960,7 @@ let sources."sort-keys-1.1.2" sources."source-list-map-2.0.1" sources."source-map-0.5.7" - sources."source-map-resolve-0.5.2" + sources."source-map-resolve-0.5.3" sources."source-map-support-0.4.18" sources."source-map-url-0.4.0" sources."split-string-3.1.0" @@ -8870,7 +8991,7 @@ let sources."sticky-sidebar-3.3.1" (sources."stream-browserify-2.0.2" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8878,12 +8999,12 @@ let sources."stream-each-1.2.3" (sources."stream-http-2.8.3" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; }) - sources."stream-shift-1.0.0" + sources."stream-shift-1.0.1" sources."strict-uri-encode-1.1.0" (sources."string-width-2.1.1" // { dependencies = [ @@ -8892,8 +9013,10 @@ let sources."strip-ansi-4.0.0" ]; }) - sources."string.prototype.trimleft-2.1.0" - sources."string.prototype.trimright-2.1.0" + sources."string.prototype.trimend-1.0.1" + sources."string.prototype.trimleft-2.1.2" + sources."string.prototype.trimright-2.1.2" + sources."string.prototype.trimstart-1.0.1" sources."string_decoder-0.10.31" sources."stringstream-0.0.6" sources."strip-ansi-0.3.0" @@ -8909,11 +9032,12 @@ let sources."js-yaml-3.7.0" ]; }) + sources."sweetalert2-9.10.12" sources."tapable-1.1.3" sources."through-2.3.8" (sources."through2-2.0.5" // { dependencies = [ - sources."readable-stream-2.3.6" + sources."readable-stream-2.3.7" sources."safe-buffer-5.1.2" sources."string_decoder-1.1.1" ]; @@ -8942,6 +9066,7 @@ let dependencies = [ sources."big.js-3.2.0" sources."colors-1.4.0" + sources."emojis-list-2.1.0" sources."enhanced-resolve-3.4.1" sources."graceful-fs-4.2.3" sources."json5-0.5.1" @@ -8949,7 +9074,7 @@ let sources."tapable-0.2.9" ]; }) - sources."tslib-1.10.0" + sources."tslib-1.11.1" sources."tty-browserify-0.0.0" sources."tunnel-agent-0.6.0" sources."tweetnacl-0.14.5" @@ -8960,11 +9085,7 @@ let sources."source-map-0.6.1" ]; }) - (sources."uglify-js-3.6.7" // { - dependencies = [ - sources."source-map-0.6.1" - ]; - }) + sources."uglify-js-3.9.1" sources."uglify-to-browserify-1.0.2" (sources."uglifyjs-webpack-plugin-1.3.0" // { dependencies = [ @@ -8974,6 +9095,10 @@ let sources."unc-path-regex-0.1.2" sources."underscore-1.7.0" sources."underscore.string-2.2.1" + sources."unicode-canonical-property-names-ecmascript-1.0.4" + sources."unicode-match-property-ecmascript-1.0.4" + sources."unicode-match-property-value-ecmascript-1.2.0" + sources."unicode-property-aliases-ecmascript-1.1.0" sources."union-value-1.0.1" sources."uniq-1.0.1" sources."uniqs-2.0.0" @@ -9012,17 +9137,17 @@ let sources."util-deprecate-1.0.2" sources."util.promisify-1.0.0" sources."utila-0.4.0" - sources."uuid-3.3.3" + sources."uuid-3.4.0" sources."v8-compile-cache-2.1.0" sources."v8flags-3.1.3" - sources."vendors-1.0.3" + sources."vendors-1.0.4" (sources."verror-1.10.0" // { dependencies = [ sources."assert-plus-1.0.0" ]; }) - sources."vm-browserify-1.1.0" - (sources."watchpack-1.6.0" // { + sources."vm-browserify-1.1.2" + (sources."watchpack-1.6.1" // { dependencies = [ sources."graceful-fs-4.2.3" ]; @@ -9030,7 +9155,7 @@ let sources."waypoints-4.0.1" (sources."webpack-4.23.1" // { dependencies = [ - sources."ajv-6.10.2" + sources."ajv-6.12.0" ]; }) (sources."webpack-cli-3.1.2" // { @@ -9086,7 +9211,7 @@ let dependencies = [ sources."find-up-3.0.0" sources."locate-path-3.0.0" - sources."p-limit-2.2.1" + sources."p-limit-2.3.0" sources."p-locate-3.0.0" sources."p-try-2.2.0" ]; diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -5,7 +5,7 @@ self: super: { "alembic" = super.buildPythonPackage { - name = "alembic-1.3.1"; + name = "alembic-1.4.2"; doCheck = false; propagatedBuildInputs = [ self."sqlalchemy" @@ -14,8 +14,8 @@ self: super: { self."python-dateutil" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/84/64/493c45119dce700a4b9eeecc436ef9e8835ab67bae6414f040cdc7b58f4b/alembic-1.3.1.tar.gz"; - sha256 = "1cl2chk5jx0rf4hmsd5lljic7iifw17yv3y5xawvp4i14jvpn9s9"; + url = "https://files.pythonhosted.org/packages/60/1e/cabc75a189de0fbb2841d0975243e59bde8b7822bacbb95008ac6fe9ad47/alembic-1.4.2.tar.gz"; + sha256 = "1gsdrzx9h7wfva200qvvsc9sn4w79mk2vs0bbnzjhxi1jw2b0nh3"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -146,15 +146,15 @@ self: super: { }; }; "bleach" = super.buildPythonPackage { - name = "bleach-3.1.0"; + name = "bleach-3.1.3"; doCheck = false; propagatedBuildInputs = [ self."six" self."webencodings" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/78/5a/0df03e8735cd9c75167528299c738702437589b9c71a849489d00ffa82e8/bleach-3.1.0.tar.gz"; - sha256 = "1yhrgrhkln8bd6gn3imj69g1h4xqah9gaz9q26crqr6gmmvpzprz"; + url = "https://files.pythonhosted.org/packages/de/09/5267f8577a92487ed43bc694476c4629c6eca2e3c93fcf690a26bfe39e1d/bleach-3.1.3.tar.gz"; + sha256 = "0al437aw4p2xp83az5hhlrp913nsf0cg6kg4qj3fjhv4wakxipzq"; }; meta = { license = [ pkgs.lib.licenses.asl20 ]; @@ -171,6 +171,17 @@ self: super: { license = [ pkgs.lib.licenses.mit ]; }; }; + "cachetools" = super.buildPythonPackage { + name = "cachetools-3.1.1"; + doCheck = false; + src = fetchurl { + url = "https://files.pythonhosted.org/packages/ae/37/7fd45996b19200e0cb2027a0b6bef4636951c4ea111bfad36c71287247f6/cachetools-3.1.1.tar.gz"; + sha256 = "16m69l6n6y1r1y7cklm92rr7v69ldig2n3lbl3j323w5jz7d78lf"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; "celery" = super.buildPythonPackage { name = "celery-4.3.0"; doCheck = false; @@ -189,11 +200,11 @@ self: super: { }; }; "certifi" = super.buildPythonPackage { - name = "certifi-2019.11.28"; + name = "certifi-2020.4.5.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/41/bf/9d214a5af07debc6acf7f3f257265618f1db242a3f8e49a9b516f24523a6/certifi-2019.11.28.tar.gz"; - sha256 = "07qg6864bk4qxa8akr967amlmsq9v310hp039mcpjx6dliylrdi5"; + url = "https://files.pythonhosted.org/packages/b8/e2/a3a86a67c3fc8249ed305fc7b7d290ebe5e4d46ad45573884761ef4dea7b/certifi-2020.4.5.1.tar.gz"; + sha256 = "06b5gfs7wmmipln8f3z928d2mmx2j4b3x7pnqmj6cvmyfh8v7z2i"; }; meta = { license = [ pkgs.lib.licenses.mpl20 { fullName = "Mozilla Public License 2.0 (MPL 2.0)"; } ]; @@ -372,6 +383,17 @@ self: super: { license = [ pkgs.lib.licenses.bsdOriginal ]; }; }; + "cssutils" = super.buildPythonPackage { + name = "cssutils-1.0.2"; + doCheck = false; + src = fetchurl { + url = "https://files.pythonhosted.org/packages/5c/0b/c5f29d29c037e97043770b5e7c740b6252993e4b57f029b3cd03c78ddfec/cssutils-1.0.2.tar.gz"; + sha256 = "1bxchrbqzapwijap0yhlxdil1w9bmwvgx77aizlkhc2mcxjg1z52"; + }; + meta = { + license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL 2.1 or later, see also http://cthedot.de/cssutils/"; } ]; + }; + }; "decorator" = super.buildPythonPackage { name = "decorator-4.1.2"; doCheck = false; @@ -429,11 +451,11 @@ self: super: { }; }; "docutils" = super.buildPythonPackage { - name = "docutils-0.14"; + name = "docutils-0.16"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/84/f4/5771e41fdf52aabebbadecc9381d11dea0fa34e4759b4071244fa094804c/docutils-0.14.tar.gz"; - sha256 = "0x22fs3pdmr42kvz6c654756wja305qv6cx1zbhwlagvxgr4xrji"; + url = "https://files.pythonhosted.org/packages/2f/e0/3d435b34abd2d62e8206171892f174b180cd37b09d57b924ca5c2ef2219d/docutils-0.16.tar.gz"; + sha256 = "1z3qliszqca9m719q3qhdkh0ghh90g500avzdgi7pl77x5h3mpn2"; }; meta = { 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 @@ self: super: { }; }; "enum34" = super.buildPythonPackage { - name = "enum34-1.1.6"; + name = "enum34-1.1.10"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz"; - sha256 = "1cgm5ng2gcfrkrm3hc22brl6chdmv67b9zvva9sfs7gn7dwc9n4a"; + url = "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz"; + sha256 = "0j7ji699fwswm4vg6w1v07fkbf8dkzdm6gfh88jvs5nqgr3sgrnc"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -631,14 +653,14 @@ self: super: { }; }; "gevent" = super.buildPythonPackage { - name = "gevent-1.4.0"; + name = "gevent-1.5.0"; doCheck = false; propagatedBuildInputs = [ self."greenlet" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/ed/27/6c49b70808f569b66ec7fac2e78f076e9b204db9cf5768740cff3d5a07ae/gevent-1.4.0.tar.gz"; - sha256 = "1lchr4akw2jkm5v4kz7bdm4wv3knkfhbfn9vkkz4s5yrkcxzmdqy"; + url = "https://files.pythonhosted.org/packages/5a/79/2c63d385d017b5dd7d70983a463dfd25befae70c824fedb857df6e72eff2/gevent-1.5.0.tar.gz"; + sha256 = "0aac3d4vhv5n4rsb6cqzq0d1xx9immqz4fmpddw35yxkwdc450dj"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -689,11 +711,11 @@ self: super: { }; }; "hupper" = super.buildPythonPackage { - name = "hupper-1.9.1"; + name = "hupper-1.10.2"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/09/3a/4f215659f31eeffe364a984dba486bfa3907bfcc54b7013bdfe825cebb5f/hupper-1.9.1.tar.gz"; - sha256 = "0pyg879fv9mbwlnbzw2a3234qqycqs9l97h5mpkmk0bvxhi2471v"; + url = "https://files.pythonhosted.org/packages/41/24/ea90fef04706e54bd1635c05c50dc9cf87cda543c59303a03e7aa7dda0ce/hupper-1.10.2.tar.gz"; + sha256 = "0am0p6g5cz6xmcaf04xq8q6dzdd9qz0phj6gcmpsckf2mcyza61q"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -711,17 +733,17 @@ self: super: { }; }; "importlib-metadata" = super.buildPythonPackage { - name = "importlib-metadata-0.23"; + name = "importlib-metadata-1.6.0"; doCheck = false; propagatedBuildInputs = [ self."zipp" + self."pathlib2" self."contextlib2" self."configparser" - self."pathlib2" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/5d/44/636bcd15697791943e2dedda0dbe098d8530a38d113b202817133e0b06c0/importlib_metadata-0.23.tar.gz"; - sha256 = "09mdqdfv5rdrwz80jh9m379gxmvk2vhjfz0fg53hid00icvxf65a"; + url = "https://files.pythonhosted.org/packages/b4/1b/baab42e3cd64c9d5caac25a9d6c054f8324cdc38975a44d600569f1f7158/importlib_metadata-1.6.0.tar.gz"; + sha256 = "07icyggasn38yv2swdrd8z6i0plazmc9adavsdkbqqj91j53ll9l"; }; meta = { license = [ pkgs.lib.licenses.asl20 ]; @@ -765,15 +787,15 @@ self: super: { }; }; "ipdb" = super.buildPythonPackage { - name = "ipdb-0.12"; + name = "ipdb-0.13.2"; doCheck = false; propagatedBuildInputs = [ self."setuptools" self."ipython" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/6d/43/c3c2e866a8803e196d6209595020a4a6db1a3c5d07c01455669497ae23d0/ipdb-0.12.tar.gz"; - sha256 = "1khr2n7xfy8hg65kj1bsrjq9g7656pp0ybfa8abpbzpdawji3qnw"; + url = "https://files.pythonhosted.org/packages/2c/bb/a3e1a441719ebd75c6dac8170d3ddba884b7ee8a5c0f9aefa7297386627a/ipdb-0.13.2.tar.gz"; + sha256 = "0jcd849rx30y3wcgzsqbn06v0yjlzvb9x3076q0yxpycdwm1ryvp"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -1074,15 +1096,15 @@ self: super: { }; }; "packaging" = super.buildPythonPackage { - name = "packaging-19.2"; + name = "packaging-20.3"; doCheck = false; propagatedBuildInputs = [ self."pyparsing" self."six" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/5a/2f/449ded84226d0e2fda8da9252e5ee7731bdf14cd338f622dfcd9934e0377/packaging-19.2.tar.gz"; - sha256 = "0izwlz9h0bw171a1chr311g2y7n657zjaf4mq4rgm8pp9lbj9f98"; + url = "https://files.pythonhosted.org/packages/65/37/83e3f492eb52d771e2820e88105f605335553fe10422cba9d256faeb1702/packaging-20.3.tar.gz"; + sha256 = "18xpablq278janh03bai9xd4kz9b0yfp6vflazn725ns9x3jna9w"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ]; @@ -1100,25 +1122,25 @@ self: super: { }; }; "paste" = super.buildPythonPackage { - name = "paste-3.2.1"; + name = "paste-3.4.0"; doCheck = false; propagatedBuildInputs = [ self."six" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/0d/86/7008b5563594e8a63763f05212a3eb84c85f0b2eff834e5697716e56bca9/Paste-3.2.1.tar.gz"; - sha256 = "1vjxr8n1p31c9x9rh8g0f34yisa9028cxpvn36q7g1s0m2b9x71x"; + url = "https://files.pythonhosted.org/packages/79/4a/45821b71dd40000507549afd1491546afad8279c0a87527c88776a794158/Paste-3.4.0.tar.gz"; + sha256 = "16sichvhyci1gaarkjs35mai8vphh7b244qm14hj1isw38nx4c03"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "pastedeploy" = super.buildPythonPackage { - name = "pastedeploy-2.0.1"; + name = "pastedeploy-2.1.0"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/19/a0/5623701df7e2478a68a1b685d1a84518024eef994cde7e4da8449a31616f/PasteDeploy-2.0.1.tar.gz"; - sha256 = "02imfbbx1mi2h546f3sr37m47dk9qizaqhzzlhx8bkzxa6fzn8yl"; + url = "https://files.pythonhosted.org/packages/c4/e9/972a1c20318b3ae9edcab11a6cef64308fbae5d0d45ab52c6f8b2b8f35b8/PasteDeploy-2.1.0.tar.gz"; + sha256 = "16qsq5y6mryslmbp5pn35x4z8z3ndp5rpgl42h226879nrw9hmg7"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -1167,14 +1189,14 @@ self: super: { }; }; "pexpect" = super.buildPythonPackage { - name = "pexpect-4.7.0"; + name = "pexpect-4.8.0"; doCheck = false; propagatedBuildInputs = [ self."ptyprocess" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/1c/b1/362a0d4235496cb42c33d1d8732b5e2c607b0129ad5fdd76f5a583b9fcb3/pexpect-4.7.0.tar.gz"; - sha256 = "1sv2rri15zwhds85a4kamwh9pj49qcxv7m4miyr4jfpfwv81yb4y"; + url = "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz"; + sha256 = "032cg337h8awydgypz6f4wx848lw8dyrj4zy988x0lyib4ws8rgw"; }; meta = { license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ]; @@ -1237,6 +1259,24 @@ self: super: { license = [ pkgs.lib.licenses.mit ]; }; }; + "premailer" = super.buildPythonPackage { + name = "premailer-3.6.1"; + doCheck = false; + propagatedBuildInputs = [ + self."lxml" + self."cssselect" + self."cssutils" + self."requests" + self."cachetools" + ]; + src = fetchurl { + url = "https://files.pythonhosted.org/packages/62/da/2f43cdf9d3d79c80c4856a12389a1f257d65fe9ccc44bc6b4383c8a18e33/premailer-3.6.1.tar.gz"; + sha256 = "08pshx7a110k4ll20x0xhpvyn3kkipkrbgxjjn7ncdxs54ihdhgw"; + }; + meta = { + license = [ pkgs.lib.licenses.psfl { fullName = "Python"; } ]; + }; + }; "prompt-toolkit" = super.buildPythonPackage { name = "prompt-toolkit-1.0.18"; doCheck = false; @@ -1253,11 +1293,11 @@ self: super: { }; }; "psutil" = super.buildPythonPackage { - name = "psutil-5.6.5"; + name = "psutil-5.7.0"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/03/9a/95c4b3d0424426e5fd94b5302ff74cea44d5d4f53466e1228ac8e73e14b4/psutil-5.6.5.tar.gz"; - sha256 = "0isil5jxwwd8awz54qk28rpgjg43i5l6yl70g40vxwa4r4m56lfh"; + url = "https://files.pythonhosted.org/packages/c4/b8/3512f0e93e0db23a71d82485ba256071ebef99b227351f0f5540f744af41/psutil-5.7.0.tar.gz"; + sha256 = "03jykdi3dgf1cdal9bv4fq9zjvzj9l9bs99gi5ar81sdl5nc2pk8"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -1348,11 +1388,11 @@ self: super: { }; }; "pycparser" = super.buildPythonPackage { - name = "pycparser-2.19"; + name = "pycparser-2.20"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz"; - sha256 = "1cr5dcj9628lkz1qlwq3fv97c25363qppkmcayqvd05dpy573259"; + url = "https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz"; + sha256 = "1w0m3xvlrzq4lkbvd1ngfm8mdw64r1yxy6n7djlw6qj5d0km6ird"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -1414,11 +1454,11 @@ self: super: { }; }; "pyparsing" = super.buildPythonPackage { - name = "pyparsing-2.4.5"; + name = "pyparsing-2.4.7"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/00/32/8076fa13e832bb4dcff379f18f228e5a53412be0631808b9ca2610c0f566/pyparsing-2.4.5.tar.gz"; - sha256 = "0fk8gsybiw1gm146mkjdjvaajwh20xwvpv4j7syh2zrnpq0j19jc"; + url = "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz"; + sha256 = "1hgc8qrbq1ymxbwfbjghv01fm3fbpjwpjwi0bcailxxzhf3yq0y2"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -1448,7 +1488,7 @@ self: super: { }; }; "pyramid-debugtoolbar" = super.buildPythonPackage { - name = "pyramid-debugtoolbar-4.5.1"; + name = "pyramid-debugtoolbar-4.6.1"; doCheck = false; propagatedBuildInputs = [ self."pyramid" @@ -1458,8 +1498,8 @@ self: super: { self."ipaddress" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/88/21/74e7fa52edc74667e29403bd0cb4f2bb74dc4014711de313868001bf639f/pyramid_debugtoolbar-4.5.1.tar.gz"; - sha256 = "0hgf6i1fzvq43m9vjdmb24nnv8fwp7sdzrx9bcwrgpy24n07am9a"; + url = "https://files.pythonhosted.org/packages/99/f6/b8603f82c18275be293921bc3a2184205056ca505747bf64ab8a0c08e124/pyramid_debugtoolbar-4.6.1.tar.gz"; + sha256 = "185z7q8n959ga5331iczwra2iljwkidfx4qn6bbd7vm3rm4w6llv"; }; meta = { license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ]; @@ -1646,15 +1686,15 @@ self: super: { }; }; "python-ldap" = super.buildPythonPackage { - name = "python-ldap-3.1.0"; + name = "python-ldap-3.2.0"; doCheck = false; propagatedBuildInputs = [ self."pyasn1" self."pyasn1-modules" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/7f/1c/28d721dff2fcd2fef9d55b40df63a00be26ec8a11e8c6fc612ae642f9cfd/python-ldap-3.1.0.tar.gz"; - sha256 = "1i97nwfnraylyn0myxlf3vciicrf5h6fymrcff9c00k581wmx5s1"; + url = "https://files.pythonhosted.org/packages/ea/93/596f875e003c770447f4b99267820a0c769dd2dc3ae3ed19afe460fcbad0/python-ldap-3.2.0.tar.gz"; + sha256 = "13nvrhp85yr0jyxixcjj012iw8l9wynxxlykm9j3alss6waln73x"; }; meta = { license = [ pkgs.lib.licenses.psfl ]; @@ -1702,11 +1742,11 @@ self: super: { }; }; "pytz" = super.buildPythonPackage { - name = "pytz-2019.2"; + name = "pytz-2019.3"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/27/c0/fbd352ca76050952a03db776d241959d5a2ee1abddfeb9e2a53fdb489be4/pytz-2019.2.tar.gz"; - sha256 = "0ckb27hhjc8i8gcdvk4d9avld62b7k52yjijc60s2m3y8cpb7h16"; + url = "https://files.pythonhosted.org/packages/82/c3/534ddba230bd4fbbd3b7a3d35f3341d014cca213f369a9940925e7e5f691/pytz-2019.3.tar.gz"; + sha256 = "1ghrk1wg45d3nymj7bf4zj03n3bh64xmczhk4pfi577hdkdhcb5h"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -1724,11 +1764,11 @@ self: super: { }; }; "redis" = super.buildPythonPackage { - name = "redis-3.3.11"; + name = "redis-3.4.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/06/ca/00557c74279d2f256d3c42cabf237631355f3a132e4c74c2000e6647ad98/redis-3.3.11.tar.gz"; - sha256 = "1hicqbi5xl92hhml82awrr2rxl9jar5fp8nbcycj9qgmsdwc43wd"; + url = "https://files.pythonhosted.org/packages/ef/2e/2c0f59891db7db087a7eeaa79bc7c7f2c039e71a2b5b0a41391e9d462926/redis-3.4.1.tar.gz"; + sha256 = "07yaj0j9fs7xdkg5bg926fa990khyigjbp31si8ai20vj8sv7kqd"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -1779,7 +1819,7 @@ self: super: { }; }; "rhodecode-enterprise-ce" = super.buildPythonPackage { - name = "rhodecode-enterprise-ce-4.18.3"; + name = "rhodecode-enterprise-ce-4.19.0"; buildInputs = [ self."pytest" self."py" @@ -1833,6 +1873,7 @@ self: super: { self."pastedeploy" self."pastescript" self."peppercorn" + self."premailer" self."psutil" self."py-bcrypt" self."pycurl" @@ -1976,11 +2017,11 @@ self: super: { }; }; "setuptools" = super.buildPythonPackage { - name = "setuptools-44.0.0"; + name = "setuptools-44.1.0"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/b0/f3/44da7482ac6da3f36f68e253cb04de37365b3dba9036a3c70773b778b485/setuptools-44.0.0.zip"; - sha256 = "025h5cnxcmda1893l6i12hrwdvs1n8r31qs6q4pkif2v7rrggfp5"; + url = "https://files.pythonhosted.org/packages/ed/7b/bbf89ca71e722b7f9464ebffe4b5ee20a9e5c9a555a56e2d3914bb9119a6/setuptools-44.1.0.zip"; + sha256 = "1jja896zvd1ppccnjbhkgagxbwchgq6vfamp6qn1hvywq6q9cjkr"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -2020,11 +2061,11 @@ self: super: { }; }; "sqlalchemy" = super.buildPythonPackage { - name = "sqlalchemy-1.3.11"; + name = "sqlalchemy-1.3.15"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/34/5c/0e1d7ad0ca52544bb12f9cb8d5cc454af45821c92160ffedd38db0a317f6/SQLAlchemy-1.3.11.tar.gz"; - sha256 = "12izpqqgy738ndn7qqn962qxi8qw2xb9vg2i880x12paklg599dg"; + url = "https://files.pythonhosted.org/packages/8c/30/4134e726dd5ed13728ff814fa91fc01c447ad8700504653fe99d91fdd34b/SQLAlchemy-1.3.15.tar.gz"; + sha256 = "0iglkvymfp35zm5pxy5kzqvcv96kkas0chqdx7xpla86sspa9k64"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -2212,11 +2253,11 @@ self: super: { }; }; "wcwidth" = super.buildPythonPackage { - name = "wcwidth-0.1.7"; + name = "wcwidth-0.1.9"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz"; - sha256 = "0pn6dflzm609m4r3i8ik5ni9ijjbb5fa3vg1n7hn6vkd49r77wrx"; + url = "https://files.pythonhosted.org/packages/25/9d/0acbed6e4a4be4fc99148f275488580968f44ddb5e69b8ceb53fc9df55a0/wcwidth-0.1.9.tar.gz"; + sha256 = "1wf5ycjx8s066rdvr0fgz4xds9a8zhs91c4jzxvvymm1c8l8cwzf"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -2234,7 +2275,7 @@ self: super: { }; }; "weberror" = super.buildPythonPackage { - name = "weberror-0.10.3"; + name = "weberror-0.13.1"; doCheck = false; propagatedBuildInputs = [ self."webob" @@ -2243,8 +2284,8 @@ self: super: { self."paste" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz"; - sha256 = "0frg4kvycqpj5bi8asfqfs6bxsr2cvjvb6b56c4d1ai1z57kbjx6"; + url = "https://files.pythonhosted.org/packages/07/0a/09ca5eb0fab5c0d17b380026babe81c96ecebb13f2b06c3203432dd7be72/WebError-0.13.1.tar.gz"; + sha256 = "0r4qvnf2r92gfnpa1kwygh4j2x6j3axg2i4an6hyxwg2gpaqp7y1"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -2277,7 +2318,7 @@ self: super: { }; }; "webtest" = super.buildPythonPackage { - name = "webtest-2.0.33"; + name = "webtest-2.0.34"; doCheck = false; propagatedBuildInputs = [ self."six" @@ -2286,8 +2327,8 @@ self: super: { self."beautifulsoup4" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/a8/b0/ffc9413b637dbe26e291429bb0f6ed731e518d0cd03da28524a8fe2e8a8f/WebTest-2.0.33.tar.gz"; - sha256 = "1l3z0cwqslsf4rcrhi2gr8kdfh74wn2dw76376i4g9i38gz8wd21"; + url = "https://files.pythonhosted.org/packages/2c/74/a0e63feee438735d628631e2b70d82280276a930637ac535479e5fad9427/WebTest-2.0.34.tar.gz"; + sha256 = "0x1y2c8z4fmpsny4hbp6ka37si2g10r5r2jwxhvv5mx7g3blq4bi"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -2327,14 +2368,14 @@ self: super: { }; }; "zipp" = super.buildPythonPackage { - name = "zipp-0.6.0"; + name = "zipp-1.2.0"; doCheck = false; propagatedBuildInputs = [ - self."more-itertools" + self."contextlib2" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/57/dd/585d728479d97d25aeeb9aa470d36a4ad8d0ba5610f84e14770128ce6ff7/zipp-0.6.0.tar.gz"; - sha256 = "13ndkf7vklw978a4gdl1yfvn8hch28429a0iam67sg4nrp5v261p"; + url = "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz"; + sha256 = "1c91lnv1bxjimh8as27hz7bghsjkkbxn1d37xq7in9c82iai0167"; }; meta = { license = [ pkgs.lib.licenses.mit ]; diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ amqp==2.5.2 babel==1.3 beaker==1.9.1 -bleach==3.1.0 +bleach==3.1.3 celery==4.3.0 channelstream==0.5.2 click==7.0 @@ -14,7 +14,7 @@ cssselect==1.0.3 cryptography==2.6.1 decorator==4.1.2 deform==2.0.8 -docutils==0.14.0 +docutils==0.16.0 dogpile.cache==0.9.0 dogpile.core==0.4.1 formencode==1.2.4 @@ -30,38 +30,39 @@ markdown==2.6.11 markupsafe==1.1.1 msgpack-python==0.5.6 pyotp==2.3.0 -packaging==19.2 +packaging==20.3 pathlib2==2.3.5 -paste==3.2.1 -pastedeploy==2.0.1 +paste==3.4.0 +pastedeploy==2.1.0 pastescript==3.2.0 peppercorn==0.6 -psutil==5.6.5 +premailer==3.6.1 +psutil==5.7.0 py-bcrypt==0.4 pycurl==7.43.0.3 pycrypto==2.6.1 pygments==2.4.2 -pyparsing==2.4.5 -pyramid-debugtoolbar==4.5.1 +pyparsing==2.4.7 +pyramid-debugtoolbar==4.6.1 pyramid-mako==1.1.0 pyramid==1.10.4 pyramid_mailer==0.15.1 python-dateutil==2.8.1 -python-ldap==3.1.0 +python-ldap==3.2.0 python-memcached==1.59 python-pam==1.8.4 python-saml==2.4.2 -pytz==2019.2 +pytz==2019.3 tzlocal==1.5.1 pyzmq==14.6.0 py-gfm==0.1.4 -redis==3.3.11 +redis==3.4.1 repoze.lru==0.7 requests==2.22.0 routes==2.4.1 simplejson==3.16.0 six==1.11.0 -sqlalchemy==1.3.11 +sqlalchemy==1.3.15 sshpubkeys==3.1.0 subprocess32==3.5.4 supervisor==4.1.0 @@ -69,7 +70,7 @@ translationstring==1.3 urllib3==1.25.2 urlobject==2.4.3 venusian==1.2.0 -weberror==0.10.3 +weberror==0.13.1 webhelpers2==2.0 webob==1.8.5 whoosh==2.7.4 @@ -94,18 +95,18 @@ jupyter-client==5.0.0 jupyter-core==4.5.0 ## cli tools -alembic==1.3.1 +alembic==1.4.2 invoke==0.13.0 bumpversion==0.5.3 ## http servers -gevent==1.4.0 +gevent==1.5.0 greenlet==0.4.15 gunicorn==19.9.0 waitress==1.3.1 ## debug -ipdb==0.12.0 +ipdb==0.13.2 ipython==5.1.0 ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version diff --git a/requirements_pinned.txt b/requirements_pinned.txt --- a/requirements_pinned.txt +++ b/requirements_pinned.txt @@ -10,9 +10,9 @@ configparser==4.0.2 contextlib2==0.6.0.post1 ecdsa==0.13.2 gnureadline==6.3.8 -hupper==1.9.1 +hupper==1.10.2 ipaddress==1.0.23 -importlib-metadata==0.23 +importlib-metadata==1.6.0 jinja2==2.9.6 jsonschema==2.6.0 pluggy==0.13.1 @@ -24,4 +24,4 @@ tempita==0.5.2 testpath==0.4.4 transaction==2.4.0 vine==1.3.0 -wcwidth==0.1.7 +wcwidth==0.1.9 diff --git a/requirements_test.txt b/requirements_test.txt --- a/requirements_test.txt +++ b/requirements_test.txt @@ -12,5 +12,5 @@ mock==3.0.5 cov-core==1.15.0 coverage==4.5.4 -webtest==2.0.33 +webtest==2.0.34 beautifulsoup4==4.6.3 diff --git a/rhodecode/VERSION b/rhodecode/VERSION --- a/rhodecode/VERSION +++ b/rhodecode/VERSION @@ -1,1 +1,1 @@ -4.18.3 \ No newline at end of file +4.19.0 \ No newline at end of file diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py --- a/rhodecode/__init__.py +++ b/rhodecode/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,17 +19,20 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import os +from collections import OrderedDict + import sys import platform VERSION = tuple(open(os.path.join( os.path.dirname(__file__), 'VERSION')).read().split('.')) -BACKENDS = { - 'hg': 'Mercurial repository', - 'git': 'Git repository', - 'svn': 'Subversion repository', -} +BACKENDS = OrderedDict() + +BACKENDS['hg'] = 'Mercurial repository' +BACKENDS['git'] = 'Git repository' +BACKENDS['svn'] = 'Subversion repository' + CELERY_ENABLED = False CELERY_EAGER = False @@ -45,7 +48,7 @@ PYRAMID_SETTINGS = {} EXTENSIONS = {} __version__ = ('.'.join((str(each) for each in VERSION[:3]))) -__dbversion__ = 103 # defines current db version for migrations +__dbversion__ = 107 # defines current db version for migrations __platform__ = platform.system() __license__ = 'AGPLv3, and Commercial License' __author__ = 'RhodeCode GmbH' diff --git a/rhodecode/api/__init__.py b/rhodecode/api/__init__.py --- a/rhodecode/api/__init__.py +++ b/rhodecode/api/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/exc.py b/rhodecode/api/exc.py --- a/rhodecode/api/exc.py +++ b/rhodecode/api/exc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/__init__.py b/rhodecode/api/tests/__init__.py --- a/rhodecode/api/tests/__init__.py +++ b/rhodecode/api/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/conftest.py b/rhodecode/api/tests/conftest.py --- a/rhodecode/api/tests/conftest.py +++ b/rhodecode/api/tests/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_add_field_to_repo.py b/rhodecode/api/tests/test_add_field_to_repo.py --- a/rhodecode/api/tests/test_add_field_to_repo.py +++ b/rhodecode/api/tests/test_add_field_to_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_add_user_to_user_group.py b/rhodecode/api/tests/test_add_user_to_user_group.py --- a/rhodecode/api/tests/test_add_user_to_user_group.py +++ b/rhodecode/api/tests/test_add_user_to_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_api.py b/rhodecode/api/tests/test_api.py --- a/rhodecode/api/tests/test_api.py +++ b/rhodecode/api/tests/test_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_cleanup_sessions.py b/rhodecode/api/tests/test_cleanup_sessions.py --- a/rhodecode/api/tests/test_cleanup_sessions.py +++ b/rhodecode/api/tests/test_cleanup_sessions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_close_pull_request.py b/rhodecode/api/tests/test_close_pull_request.py --- a/rhodecode/api/tests/test_close_pull_request.py +++ b/rhodecode/api/tests/test_close_pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_comment_commit.py b/rhodecode/api/tests/test_comment_commit.py --- a/rhodecode/api/tests/test_comment_commit.py +++ b/rhodecode/api/tests/test_comment_commit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_comment_pull_request.py b/rhodecode/api/tests/test_comment_pull_request.py --- a/rhodecode/api/tests/test_comment_pull_request.py +++ b/rhodecode/api/tests/test_comment_pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_gist.py b/rhodecode/api/tests/test_create_gist.py --- a/rhodecode/api/tests/test_create_gist.py +++ b/rhodecode/api/tests/test_create_gist.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_pull_request.py b/rhodecode/api/tests/test_create_pull_request.py --- a/rhodecode/api/tests/test_create_pull_request.py +++ b/rhodecode/api/tests/test_create_pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_repo.py b/rhodecode/api/tests/test_create_repo.py --- a/rhodecode/api/tests/test_create_repo.py +++ b/rhodecode/api/tests/test_create_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_repo_group.py b/rhodecode/api/tests/test_create_repo_group.py --- a/rhodecode/api/tests/test_create_repo_group.py +++ b/rhodecode/api/tests/test_create_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_user.py b/rhodecode/api/tests/test_create_user.py --- a/rhodecode/api/tests/test_create_user.py +++ b/rhodecode/api/tests/test_create_user.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_user_group.py b/rhodecode/api/tests/test_create_user_group.py --- a/rhodecode/api/tests/test_create_user_group.py +++ b/rhodecode/api/tests/test_create_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_gist.py b/rhodecode/api/tests/test_delete_gist.py --- a/rhodecode/api/tests/test_delete_gist.py +++ b/rhodecode/api/tests/test_delete_gist.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_repo.py b/rhodecode/api/tests/test_delete_repo.py --- a/rhodecode/api/tests/test_delete_repo.py +++ b/rhodecode/api/tests/test_delete_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_repo_group.py b/rhodecode/api/tests/test_delete_repo_group.py --- a/rhodecode/api/tests/test_delete_repo_group.py +++ b/rhodecode/api/tests/test_delete_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_user.py b/rhodecode/api/tests/test_delete_user.py --- a/rhodecode/api/tests/test_delete_user.py +++ b/rhodecode/api/tests/test_delete_user.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_user_group.py b/rhodecode/api/tests/test_delete_user_group.py --- a/rhodecode/api/tests/test_delete_user_group.py +++ b/rhodecode/api/tests/test_delete_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_deprecated_api.py b/rhodecode/api/tests/test_deprecated_api.py --- a/rhodecode/api/tests/test_deprecated_api.py +++ b/rhodecode/api/tests/test_deprecated_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_fork_repo.py b/rhodecode/api/tests/test_fork_repo.py --- a/rhodecode/api/tests/test_fork_repo.py +++ b/rhodecode/api/tests/test_fork_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_fts_search.py b/rhodecode/api/tests/test_fts_search.py --- a/rhodecode/api/tests/test_fts_search.py +++ b/rhodecode/api/tests/test_fts_search.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_gist.py b/rhodecode/api/tests/test_get_gist.py --- a/rhodecode/api/tests/test_get_gist.py +++ b/rhodecode/api/tests/test_get_gist.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_gists.py b/rhodecode/api/tests/test_get_gists.py --- a/rhodecode/api/tests/test_get_gists.py +++ b/rhodecode/api/tests/test_get_gists.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_ip.py b/rhodecode/api/tests/test_get_ip.py --- a/rhodecode/api/tests/test_get_ip.py +++ b/rhodecode/api/tests/test_get_ip.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_locks.py b/rhodecode/api/tests/test_get_locks.py --- a/rhodecode/api/tests/test_get_locks.py +++ b/rhodecode/api/tests/test_get_locks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_method.py b/rhodecode/api/tests/test_get_method.py --- a/rhodecode/api/tests/test_get_method.py +++ b/rhodecode/api/tests/test_get_method.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_pull_request.py b/rhodecode/api/tests/test_get_pull_request.py --- a/rhodecode/api/tests/test_get_pull_request.py +++ b/rhodecode/api/tests/test_get_pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_pull_request_comments.py b/rhodecode/api/tests/test_get_pull_request_comments.py --- a/rhodecode/api/tests/test_get_pull_request_comments.py +++ b/rhodecode/api/tests/test_get_pull_request_comments.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -60,7 +60,10 @@ class TestGetPullRequestComments(object) 'comment_text': 'Auto status change to |new_status|\n\n.. |new_status| replace:: *"Under Review"*', 'comment_type': 'note', 'comment_resolved_by': None, - 'pull_request_version': None} + 'pull_request_version': None, + 'comment_commit_id': None, + 'comment_pull_request_id': pull_request.pull_request_id + } ] assert_ok(id_, expected, response.body) diff --git a/rhodecode/api/tests/test_get_pull_requests.py b/rhodecode/api/tests/test_get_pull_requests.py --- a/rhodecode/api/tests/test_get_pull_requests.py +++ b/rhodecode/api/tests/test_get_pull_requests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo.py b/rhodecode/api/tests/test_get_repo.py --- a/rhodecode/api/tests/test_get_repo.py +++ b/rhodecode/api/tests/test_get_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_changeset.py b/rhodecode/api/tests/test_get_repo_changeset.py --- a/rhodecode/api/tests/test_get_repo_changeset.py +++ b/rhodecode/api/tests/test_get_repo_changeset.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_comments.py b/rhodecode/api/tests/test_get_repo_comments.py --- a/rhodecode/api/tests/test_get_repo_comments.py +++ b/rhodecode/api/tests/test_get_repo_comments.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_group.py b/rhodecode/api/tests/test_get_repo_group.py --- a/rhodecode/api/tests/test_get_repo_group.py +++ b/rhodecode/api/tests/test_get_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_groups.py b/rhodecode/api/tests/test_get_repo_groups.py --- a/rhodecode/api/tests/test_get_repo_groups.py +++ b/rhodecode/api/tests/test_get_repo_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_nodes.py b/rhodecode/api/tests/test_get_repo_nodes.py --- a/rhodecode/api/tests/test_get_repo_nodes.py +++ b/rhodecode/api/tests/test_get_repo_nodes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_refs.py b/rhodecode/api/tests/test_get_repo_refs.py --- a/rhodecode/api/tests/test_get_repo_refs.py +++ b/rhodecode/api/tests/test_get_repo_refs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repos.py b/rhodecode/api/tests/test_get_repos.py --- a/rhodecode/api/tests/test_get_repos.py +++ b/rhodecode/api/tests/test_get_repos.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_server_info.py b/rhodecode/api/tests/test_get_server_info.py --- a/rhodecode/api/tests/test_get_server_info.py +++ b/rhodecode/api/tests/test_get_server_info.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_user.py b/rhodecode/api/tests/test_get_user.py --- a/rhodecode/api/tests/test_get_user.py +++ b/rhodecode/api/tests/test_get_user.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_user_group.py b/rhodecode/api/tests/test_get_user_group.py --- a/rhodecode/api/tests/test_get_user_group.py +++ b/rhodecode/api/tests/test_get_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_user_groups.py b/rhodecode/api/tests/test_get_user_groups.py --- a/rhodecode/api/tests/test_get_user_groups.py +++ b/rhodecode/api/tests/test_get_user_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_users.py b/rhodecode/api/tests/test_get_users.py --- a/rhodecode/api/tests/test_get_users.py +++ b/rhodecode/api/tests/test_get_users.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_group_permission.py b/rhodecode/api/tests/test_grant_user_group_permission.py --- a/rhodecode/api/tests/test_grant_user_group_permission.py +++ b/rhodecode/api/tests/test_grant_user_group_permission.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py b/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py --- a/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py +++ b/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py b/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py --- a/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py +++ b/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_permission.py b/rhodecode/api/tests/test_grant_user_permission.py --- a/rhodecode/api/tests/test_grant_user_permission.py +++ b/rhodecode/api/tests/test_grant_user_permission.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py b/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py --- a/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py +++ b/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_permission_to_user_group.py b/rhodecode/api/tests/test_grant_user_permission_to_user_group.py --- a/rhodecode/api/tests/test_grant_user_permission_to_user_group.py +++ b/rhodecode/api/tests/test_grant_user_permission_to_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_invalidate_cache.py b/rhodecode/api/tests/test_invalidate_cache.py --- a/rhodecode/api/tests/test_invalidate_cache.py +++ b/rhodecode/api/tests/test_invalidate_cache.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_merge_pull_request.py b/rhodecode/api/tests/test_merge_pull_request.py --- a/rhodecode/api/tests/test_merge_pull_request.py +++ b/rhodecode/api/tests/test_merge_pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_pull.py b/rhodecode/api/tests/test_pull.py --- a/rhodecode/api/tests/test_pull.py +++ b/rhodecode/api/tests/test_pull.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_remove_field_from_repo.py b/rhodecode/api/tests/test_remove_field_from_repo.py --- a/rhodecode/api/tests/test_remove_field_from_repo.py +++ b/rhodecode/api/tests/test_remove_field_from_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_remove_user_from_user_group.py b/rhodecode/api/tests/test_remove_user_from_user_group.py --- a/rhodecode/api/tests/test_remove_user_from_user_group.py +++ b/rhodecode/api/tests/test_remove_user_from_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_repo_locking.py b/rhodecode/api/tests/test_repo_locking.py --- a/rhodecode/api/tests/test_repo_locking.py +++ b/rhodecode/api/tests/test_repo_locking.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_rescan_repos.py b/rhodecode/api/tests/test_rescan_repos.py --- a/rhodecode/api/tests/test_rescan_repos.py +++ b/rhodecode/api/tests/test_rescan_repos.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_group_permission.py b/rhodecode/api/tests/test_revoke_user_group_permission.py --- a/rhodecode/api/tests/test_revoke_user_group_permission.py +++ b/rhodecode/api/tests/test_revoke_user_group_permission.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py b/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py --- a/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py +++ b/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py b/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py --- a/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py +++ b/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_permission.py b/rhodecode/api/tests/test_revoke_user_permission.py --- a/rhodecode/api/tests/test_revoke_user_permission.py +++ b/rhodecode/api/tests/test_revoke_user_permission.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py b/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py --- a/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py +++ b/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py b/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py --- a/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py +++ b/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_store_exception.py b/rhodecode/api/tests/test_store_exception.py --- a/rhodecode/api/tests/test_store_exception.py +++ b/rhodecode/api/tests/test_store_exception.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_pull_request.py b/rhodecode/api/tests/test_update_pull_request.py --- a/rhodecode/api/tests/test_update_pull_request.py +++ b/rhodecode/api/tests/test_update_pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_repo.py b/rhodecode/api/tests/test_update_repo.py --- a/rhodecode/api/tests/test_update_repo.py +++ b/rhodecode/api/tests/test_update_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_repo_group.py b/rhodecode/api/tests/test_update_repo_group.py --- a/rhodecode/api/tests/test_update_repo_group.py +++ b/rhodecode/api/tests/test_update_repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_user.py b/rhodecode/api/tests/test_update_user.py --- a/rhodecode/api/tests/test_update_user.py +++ b/rhodecode/api/tests/test_update_user.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_user_group.py b/rhodecode/api/tests/test_update_user_group.py --- a/rhodecode/api/tests/test_update_user_group.py +++ b/rhodecode/api/tests/test_update_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_utils.py b/rhodecode/api/tests/test_utils.py --- a/rhodecode/api/tests/test_utils.py +++ b/rhodecode/api/tests/test_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/utils.py b/rhodecode/api/tests/utils.py --- a/rhodecode/api/tests/utils.py +++ b/rhodecode/api/tests/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -49,7 +49,8 @@ def assert_call_ok(id_, given): def assert_ok(id_, expected, given): given = json.loads(given) if given.get('error'): - pytest.fail("Unexpected ERROR in success response: {}".format(given['error'])) + err = given['error'] + pytest.fail(u"Unexpected ERROR in success response: {}".format(err)) expected = jsonify({ 'id': id_, diff --git a/rhodecode/api/utils.py b/rhodecode/api/utils.py --- a/rhodecode/api/utils.py +++ b/rhodecode/api/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/__init__.py b/rhodecode/api/views/__init__.py --- a/rhodecode/api/views/__init__.py +++ b/rhodecode/api/views/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015-2019 RhodeCode GmbH +# Copyright (C) 2015-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/deprecated_api.py b/rhodecode/api/views/deprecated_api.py --- a/rhodecode/api/views/deprecated_api.py +++ b/rhodecode/api/views/deprecated_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/gist_api.py b/rhodecode/api/views/gist_api.py --- a/rhodecode/api/views/gist_api.py +++ b/rhodecode/api/views/gist_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/pull_request_api.py b/rhodecode/api/views/pull_request_api.py --- a/rhodecode/api/views/pull_request_api.py +++ b/rhodecode/api/views/pull_request_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -379,7 +379,9 @@ def get_pull_request_comments( }, "comment_text": "Example text", "comment_type": null, - "pull_request_version": null + "pull_request_version": null, + "comment_commit_id": None, + "comment_pull_request_id": } ], error : null @@ -684,28 +686,9 @@ def create_pull_request( full_source_ref = resolve_ref_or_error(source_ref, source_db_repo) full_target_ref = resolve_ref_or_error(target_ref, target_db_repo) - source_scm = source_db_repo.scm_instance() - target_scm = target_db_repo.scm_instance() - source_commit = get_commit_or_error(full_source_ref, source_db_repo) target_commit = get_commit_or_error(full_target_ref, target_db_repo) - ancestor = source_scm.get_common_ancestor( - source_commit.raw_id, target_commit.raw_id, target_scm) - if not ancestor: - raise JSONRPCError('no common ancestor found') - - # recalculate target ref based on ancestor - target_ref_type, target_ref_name, __ = full_target_ref.split(':') - full_target_ref = ':'.join((target_ref_type, target_ref_name, ancestor)) - - commit_ranges = target_scm.compare( - target_commit.raw_id, source_commit.raw_id, source_scm, - merge=True, pre_load=[]) - - if not commit_ranges: - raise JSONRPCError('no commits found') - reviewer_objects = Optional.extract(reviewers) or [] # serialize and validate passed in given reviewers @@ -725,16 +708,16 @@ def create_pull_request( PullRequestModel().get_reviewer_functions() # recalculate reviewers logic, to make sure we can validate this - reviewer_rules = get_default_reviewers_data( + default_reviewers_data = get_default_reviewers_data( owner, source_db_repo, source_commit, target_db_repo, target_commit) # now MERGE our given with the calculated - reviewer_objects = reviewer_rules['reviewers'] + reviewer_objects + reviewer_objects = default_reviewers_data['reviewers'] + reviewer_objects try: reviewers = validate_default_reviewers( - reviewer_objects, reviewer_rules) + reviewer_objects, default_reviewers_data) except ValueError as e: raise JSONRPCError('Reviewers Validation: {}'.format(e)) @@ -746,6 +729,24 @@ def create_pull_request( source_ref=title_source_ref, target=target_repo ) + + diff_info = default_reviewers_data['diff_info'] + common_ancestor_id = diff_info['ancestor'] + commits = diff_info['commits'] + + if not common_ancestor_id: + raise JSONRPCError('no common ancestor found') + + if not commits: + raise JSONRPCError('no commits found') + + # NOTE(marcink): reversed is consistent with how we open it in the WEB interface + revisions = [commit.raw_id for commit in reversed(commits)] + + # recalculate target ref based on ancestor + target_ref_type, target_ref_name, __ = full_target_ref.split(':') + full_target_ref = ':'.join((target_ref_type, target_ref_name, common_ancestor_id)) + # fetch renderer, if set fallback to plain in case of PR rc_config = SettingsModel().get_all_settings() default_system_renderer = rc_config.get('rhodecode_markup_renderer', 'plain') @@ -758,12 +759,13 @@ def create_pull_request( source_ref=full_source_ref, target_repo=target_repo, target_ref=full_target_ref, - revisions=[commit.raw_id for commit in reversed(commit_ranges)], + common_ancestor_id=common_ancestor_id, + revisions=revisions, reviewers=reviewers, title=title, description=description, description_renderer=description_renderer, - reviewer_data=reviewer_rules, + reviewer_data=default_reviewers_data, auth_user=apiuser ) diff --git a/rhodecode/api/views/repo_api.py b/rhodecode/api/views/repo_api.py --- a/rhodecode/api/views/repo_api.py +++ b/rhodecode/api/views/repo_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -33,7 +33,7 @@ from rhodecode.lib import audit_logger, from rhodecode.lib import repo_maintenance from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi from rhodecode.lib.celerylib.utils import get_task_id -from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int +from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int, safe_unicode from rhodecode.lib.ext_json import json from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError from rhodecode.lib.vcs import RepositoryError @@ -573,6 +573,7 @@ def get_repo_file(request, apiuser, repo elif details == 'full': extended_info = content = True + file_path = safe_unicode(file_path) try: # check if repo is not empty by any chance, skip quicker if it is. _scm = repo.scm_instance() @@ -583,12 +584,12 @@ def get_repo_file(request, apiuser, repo repo, commit_id, file_path, extended_info=extended_info, content=content, max_file_bytes=max_file_bytes, cache=cache) except NodeDoesNotExistError: - raise JSONRPCError('There is no file in repo: `{}` at path `{}` for commit: `{}`'.format( + raise JSONRPCError(u'There is no file in repo: `{}` at path `{}` for commit: `{}`'.format( repo.repo_name, file_path, commit_id)) except Exception: - log.exception("Exception occurred while trying to get repo %s file", + log.exception(u"Exception occurred while trying to get repo %s file", repo.repo_name) - raise JSONRPCError('failed to get repo: `{}` file at path {}'.format( + raise JSONRPCError(u'failed to get repo: `{}` file at path {}'.format( repo.repo_name, file_path)) return node @@ -1599,7 +1600,8 @@ def comment_commit( validate_repo_permissions(apiuser, repoid, repo, _perms) try: - commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id + commit = repo.scm_instance().get_commit(commit_id=commit_id) + commit_id = commit.raw_id except Exception as e: log.exception('Failed to fetch commit') raise JSONRPCError(safe_str(e)) @@ -1655,10 +1657,14 @@ def comment_commit( except StatusChangeOnClosedPullRequestError: log.exception( "Exception occurred while trying to change repo commit status") - msg = ('Changing status on a changeset associated with ' + msg = ('Changing status on a commit associated with ' 'a closed pull request is not allowed') raise JSONRPCError(msg) + CommentsModel().trigger_commit_comment_hook( + repo, apiuser, 'create', + data={'comment': comment, 'commit': commit}) + Session().commit() return { 'msg': ( diff --git a/rhodecode/api/views/repo_group_api.py b/rhodecode/api/views/repo_group_api.py --- a/rhodecode/api/views/repo_group_api.py +++ b/rhodecode/api/views/repo_group_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/search_api.py b/rhodecode/api/views/search_api.py --- a/rhodecode/api/views/search_api.py +++ b/rhodecode/api/views/search_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -22,10 +22,12 @@ import logging from rhodecode.api import jsonrpc_method -from rhodecode.api.exc import JSONRPCValidationError -from rhodecode.api.utils import Optional +from rhodecode.api.exc import JSONRPCValidationError, JSONRPCForbidden +from rhodecode.api.utils import Optional, has_superadmin_permission from rhodecode.lib.index import searcher_from_config +from rhodecode.lib.user_log_filter import user_log_filter from rhodecode.model import validation_schema +from rhodecode.model.db import joinedload, UserLog from rhodecode.model.validation_schema.schemas import search_schema log = logging.getLogger(__name__) @@ -116,3 +118,35 @@ def search(request, apiuser, search_quer colander_exc=validation_schema.Invalid(node, search_result['error'])) return data + + +@jsonrpc_method() +def get_audit_logs(request, apiuser, query): + """ + return full audit logs based on the query. + + Please see `example query in admin > settings > audit logs` for examples + + :param apiuser: This is filled automatically from the |authtoken|. + :type apiuser: AuthUser + :param query: filter query, example: action:repo.artifact.add date:[20200401 TO 20200601]" + :type query: str + """ + + if not has_superadmin_permission(apiuser): + raise JSONRPCForbidden() + + filter_term = query + ret = [] + + # show all user actions + user_log = UserLog.query() \ + .options(joinedload(UserLog.user)) \ + .options(joinedload(UserLog.repository)) \ + .order_by(UserLog.action_date.desc()) + + audit_log = user_log_filter(user_log, filter_term) + + for entry in audit_log: + ret.append(entry) + return ret diff --git a/rhodecode/api/views/server_api.py b/rhodecode/api/views/server_api.py --- a/rhodecode/api/views/server_api.py +++ b/rhodecode/api/views/server_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/testing_api.py b/rhodecode/api/views/testing_api.py --- a/rhodecode/api/views/testing_api.py +++ b/rhodecode/api/views/testing_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/user_api.py b/rhodecode/api/views/user_api.py --- a/rhodecode/api/views/user_api.py +++ b/rhodecode/api/views/user_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/user_group_api.py b/rhodecode/api/views/user_group_api.py --- a/rhodecode/api/views/user_group_api.py +++ b/rhodecode/api/views/user_group_api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/__init__.py b/rhodecode/apps/__init__.py --- a/rhodecode/apps/__init__.py +++ b/rhodecode/apps/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -396,7 +396,11 @@ class PathFilter(object): def path_access_allowed(self, path): log.debug('Checking ACL permissions for PathFilter for `%s`', path) if self.permission_checker: - return path and self.permission_checker.has_access(path) + has_access = path and self.permission_checker.has_access(path) + log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access) + return has_access + + log.debug('ACL permissions checker not enabled, skipping...') return True def filter_patchset(self, patchset): diff --git a/rhodecode/apps/_base/interfaces.py b/rhodecode/apps/_base/interfaces.py --- a/rhodecode/apps/_base/interfaces.py +++ b/rhodecode/apps/_base/interfaces.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/navigation.py b/rhodecode/apps/_base/navigation.py --- a/rhodecode/apps/_base/navigation.py +++ b/rhodecode/apps/_base/navigation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/subscribers.py b/rhodecode/apps/_base/subscribers.py --- a/rhodecode/apps/_base/subscribers.py +++ b/rhodecode/apps/_base/subscribers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -25,6 +25,15 @@ from rhodecode.lib import rc_cache log = logging.getLogger(__name__) +# names of namespaces used for different permission related cached +# during flush operation we need to take care of all those +cache_namespaces = [ + 'cache_user_auth.{}', + 'cache_user_repo_acl_ids.{}', + 'cache_user_user_group_acl_ids.{}', + 'cache_user_repo_group_acl_ids.{}' +] + def trigger_user_permission_flush(event): """ @@ -35,9 +44,11 @@ def trigger_user_permission_flush(event) affected_user_ids = set(event.user_ids) for user_id in affected_user_ids: - cache_namespace_uid = 'cache_user_auth.{}'.format(user_id) - del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid) - log.debug('Deleted %s cache keys for user_id: %s', del_keys, user_id) + for cache_namespace_uid_tmpl in cache_namespaces: + cache_namespace_uid = cache_namespace_uid_tmpl.format(user_id) + del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid) + log.debug('Deleted %s cache keys for user_id: %s and namespace %s', + del_keys, user_id, cache_namespace_uid) def includeme(config): diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -26,7 +26,6 @@ def admin_routes(config): """ Admin prefixed routes """ - config.add_route( name='admin_audit_logs', pattern='/audit_logs') @@ -291,12 +290,22 @@ def admin_routes(config): pattern='/users/{user_id:\d+}/create_repo_group', user_route=True) + # user notice + config.add_route( + name='user_notice_dismiss', + pattern='/users/{user_id:\d+}/notice_dismiss', + user_route=True) + # user auth tokens config.add_route( name='edit_user_auth_tokens', pattern='/users/{user_id:\d+}/edit/auth_tokens', user_route=True) config.add_route( + name='edit_user_auth_tokens_view', + pattern='/users/{user_id:\d+}/edit/auth_tokens/view', + user_route=True) + config.add_route( name='edit_user_auth_tokens_add', pattern='/users/{user_id:\d+}/edit/auth_tokens/new', user_route=True) diff --git a/rhodecode/apps/admin/tests/test_admin_audit_logs.py b/rhodecode/apps/admin/tests/test_admin_audit_logs.py --- a/rhodecode/apps/admin/tests/test_admin_audit_logs.py +++ b/rhodecode/apps/admin/tests/test_admin_audit_logs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_auth_settings.py b/rhodecode/apps/admin/tests/test_admin_auth_settings.py --- a/rhodecode/apps/admin/tests/test_admin_auth_settings.py +++ b/rhodecode/apps/admin/tests/test_admin_auth_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_defaults.py b/rhodecode/apps/admin/tests/test_admin_defaults.py --- a/rhodecode/apps/admin/tests/test_admin_defaults.py +++ b/rhodecode/apps/admin/tests/test_admin_defaults.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_main_views.py b/rhodecode/apps/admin/tests/test_admin_main_views.py --- a/rhodecode/apps/admin/tests/test_admin_main_views.py +++ b/rhodecode/apps/admin/tests/test_admin_main_views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_permissions.py b/rhodecode/apps/admin/tests/test_admin_permissions.py --- a/rhodecode/apps/admin/tests/test_admin_permissions.py +++ b/rhodecode/apps/admin/tests/test_admin_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -228,7 +228,7 @@ class TestAdminPermissionsController(Tes self.log_user() # ADD - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() self.app.post( route_path('edit_user_ips_add', user_id=default_user_id), params={'new_ip': '0.0.0.0/24', 'csrf_token': self.csrf_token}) @@ -238,7 +238,7 @@ class TestAdminPermissionsController(Tes response.mustcontain('0.0.0.0 - 0.0.0.255') # DELETE - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() del_ip_id = UserIpMap.query().filter(UserIpMap.user_id == default_user_id).first().ip_id diff --git a/rhodecode/apps/admin/tests/test_admin_repos.py b/rhodecode/apps/admin/tests/test_admin_repos.py --- a/rhodecode/apps/admin/tests/test_admin_repos.py +++ b/rhodecode/apps/admin/tests/test_admin_repos.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -84,15 +84,13 @@ class TestAdminRepos(object): with mock.patch('rhodecode.BACKENDS', {'git': 'git'}): response = self.app.get(route_path('repo_new'), status=200) assert_response = response.assert_response() - element = assert_response.get_element('#repo_type') - assert element.text_content() == '\ngit\n' + element = assert_response.get_element('[name=repo_type]') + assert element.get('value') == 'git' def test_create_page_non_restricted_backends(self, autologin_user, backend): response = self.app.get(route_path('repo_new'), status=200) assert_response = response.assert_response() - assert_response.element_contains('#repo_type', 'git') - assert_response.element_contains('#repo_type', 'svn') - assert_response.element_contains('#repo_type', 'hg') + assert ['hg', 'git', 'svn'] == [x.get('value') for x in assert_response.get_elements('[name=repo_type]')] @pytest.mark.parametrize( "suffix", [u'', u'xxa'], ids=['', 'non-ascii']) diff --git a/rhodecode/apps/admin/tests/test_admin_repository_groups.py b/rhodecode/apps/admin/tests/test_admin_repository_groups.py --- a/rhodecode/apps/admin/tests/test_admin_repository_groups.py +++ b/rhodecode/apps/admin/tests/test_admin_repository_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_settings.py b/rhodecode/apps/admin/tests/test_admin_settings.py --- a/rhodecode/apps/admin/tests/test_admin_settings.py +++ b/rhodecode/apps/admin/tests/test_admin_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_user_groups.py b/rhodecode/apps/admin/tests/test_admin_user_groups.py --- a/rhodecode/apps/admin/tests/test_admin_user_groups.py +++ b/rhodecode/apps/admin/tests/test_admin_user_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_users.py b/rhodecode/apps/admin/tests/test_admin_users.py --- a/rhodecode/apps/admin/tests/test_admin_users.py +++ b/rhodecode/apps/admin/tests/test_admin_users.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -143,7 +143,7 @@ class TestAdminUsersView(TestController) response = self.app.get( route_path('edit_user_auth_tokens', user_id=user_id)) for token in auth_tokens: - response.mustcontain(token) + response.mustcontain(token[:4]) response.mustcontain('never') @pytest.mark.parametrize("desc, lifetime", [ @@ -165,7 +165,7 @@ class TestAdminUsersView(TestController) response = response.follow() user = User.get(user_id) for auth_token in user.auth_tokens: - response.mustcontain(auth_token) + response.mustcontain(auth_token[:4]) def test_delete_auth_token(self, user_util): self.log_user() diff --git a/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py b/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py --- a/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py +++ b/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/__init__.py b/rhodecode/apps/admin/views/__init__.py --- a/rhodecode/apps/admin/views/__init__.py +++ b/rhodecode/apps/admin/views/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/audit_logs.py b/rhodecode/apps/admin/views/audit_logs.py --- a/rhodecode/apps/admin/views/audit_logs.py +++ b/rhodecode/apps/admin/views/audit_logs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/defaults.py b/rhodecode/apps/admin/views/defaults.py --- a/rhodecode/apps/admin/views/defaults.py +++ b/rhodecode/apps/admin/views/defaults.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/exception_tracker.py b/rhodecode/apps/admin/views/exception_tracker.py --- a/rhodecode/apps/admin/views/exception_tracker.py +++ b/rhodecode/apps/admin/views/exception_tracker.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2018-2019 RhodeCode GmbH +# Copyright (C) 2018-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/main_views.py b/rhodecode/apps/admin/views/main_views.py --- a/rhodecode/apps/admin/views/main_views.py +++ b/rhodecode/apps/admin/views/main_views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/open_source_licenses.py b/rhodecode/apps/admin/views/open_source_licenses.py --- a/rhodecode/apps/admin/views/open_source_licenses.py +++ b/rhodecode/apps/admin/views/open_source_licenses.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/permissions.py b/rhodecode/apps/admin/views/permissions.py --- a/rhodecode/apps/admin/views/permissions.py +++ b/rhodecode/apps/admin/views/permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -142,7 +142,7 @@ class AdminPermissionsView(BaseAppView, h.flash(_('Error occurred during update of permissions'), category='error') - affected_user_ids = [User.get_default_user().user_id] + affected_user_ids = [User.get_default_user_id()] PermissionModel().trigger_permission_flush(affected_user_ids) raise HTTPFound(h.route_path('admin_permissions_application')) @@ -218,7 +218,7 @@ class AdminPermissionsView(BaseAppView, h.flash(_('Error occurred during update of permissions'), category='error') - affected_user_ids = [User.get_default_user().user_id] + affected_user_ids = [User.get_default_user_id()] PermissionModel().trigger_permission_flush(affected_user_ids) raise HTTPFound(h.route_path('admin_permissions_object')) @@ -320,7 +320,7 @@ class AdminPermissionsView(BaseAppView, h.flash(_('Error occurred during update of permissions'), category='error') - affected_user_ids = [User.get_default_user().user_id] + affected_user_ids = [User.get_default_user_id()] PermissionModel().trigger_permission_flush(affected_user_ids) raise HTTPFound(h.route_path('admin_permissions_global')) diff --git a/rhodecode/apps/admin/views/process_management.py b/rhodecode/apps/admin/views/process_management.py --- a/rhodecode/apps/admin/views/process_management.py +++ b/rhodecode/apps/admin/views/process_management.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/repo_groups.py b/rhodecode/apps/admin/views/repo_groups.py --- a/rhodecode/apps/admin/views/repo_groups.py +++ b/rhodecode/apps/admin/views/repo_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/repositories.py b/rhodecode/apps/admin/views/repositories.py --- a/rhodecode/apps/admin/views/repositories.py +++ b/rhodecode/apps/admin/views/repositories.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/sessions.py b/rhodecode/apps/admin/views/sessions.py --- a/rhodecode/apps/admin/views/sessions.py +++ b/rhodecode/apps/admin/views/sessions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/svn_config.py b/rhodecode/apps/admin/views/svn_config.py --- a/rhodecode/apps/admin/views/svn_config.py +++ b/rhodecode/apps/admin/views/svn_config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -41,9 +41,9 @@ class SvnConfigAdminSettingsView(BaseApp def vcs_svn_generate_config(self): _ = self.request.translate try: - generate_mod_dav_svn_config(self.request.registry) + file_path = generate_mod_dav_svn_config(self.request.registry) msg = { - 'message': _('Apache configuration for Subversion generated.'), + 'message': _('Apache configuration for Subversion generated at `{}`.').format(file_path), 'level': 'success', } except Exception: diff --git a/rhodecode/apps/admin/views/system_info.py b/rhodecode/apps/admin/views/system_info.py --- a/rhodecode/apps/admin/views/system_info.py +++ b/rhodecode/apps/admin/views/system_info.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/user_groups.py b/rhodecode/apps/admin/views/user_groups.py --- a/rhodecode/apps/admin/views/user_groups.py +++ b/rhodecode/apps/admin/views/user_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/users.py b/rhodecode/apps/admin/views/users.py --- a/rhodecode/apps/admin/views/users.py +++ b/rhodecode/apps/admin/views/users.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -34,12 +34,13 @@ from rhodecode.apps.ssh_support import S from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin from rhodecode.authentication.plugins import auth_rhodecode from rhodecode.events import trigger -from rhodecode.model.db import true +from rhodecode.model.db import true, UserNotice from rhodecode.lib import audit_logger, rc_cache from rhodecode.lib.exceptions import ( UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException, - UserOwnsUserGroupsException, DefaultUserException) + UserOwnsUserGroupsException, UserOwnsPullRequestsException, + UserOwnsArtifactsException, DefaultUserException) from rhodecode.lib.ext_json import json from rhodecode.lib.auth import ( LoginRequired, HasPermissionAllDecorator, CSRFRequired) @@ -377,11 +378,13 @@ class UsersView(UserAppView): _repos = c.user.repositories _repo_groups = c.user.repository_groups _user_groups = c.user.user_groups + _pull_requests = c.user.user_pull_requests _artifacts = c.user.artifacts handle_repos = None handle_repo_groups = None handle_user_groups = None + handle_pull_requests = None handle_artifacts = None # calls for flash of handle based on handle case detach or delete @@ -412,6 +415,15 @@ class UsersView(UserAppView): h.flash(_('Deleted %s user groups') % len(_user_groups), category='success') + def set_handle_flash_pull_requests(): + handle = handle_pull_requests + if handle == 'detach': + h.flash(_('Detached %s pull requests') % len(_pull_requests), + category='success') + elif handle == 'delete': + h.flash(_('Deleted %s pull requests') % len(_pull_requests), + category='success') + def set_handle_flash_artifacts(): handle = handle_artifacts if handle == 'detach': @@ -421,6 +433,12 @@ class UsersView(UserAppView): h.flash(_('Deleted %s artifacts') % len(_artifacts), category='success') + handle_user = User.get_first_super_admin() + handle_user_id = safe_int(self.request.POST.get('detach_user_id')) + if handle_user_id: + # NOTE(marcink): we get new owner for objects... + handle_user = User.get_or_404(handle_user_id) + if _repos and self.request.POST.get('user_repos'): handle_repos = self.request.POST['user_repos'] @@ -430,16 +448,25 @@ class UsersView(UserAppView): if _user_groups and self.request.POST.get('user_user_groups'): handle_user_groups = self.request.POST['user_user_groups'] + if _pull_requests and self.request.POST.get('user_pull_requests'): + handle_pull_requests = self.request.POST['user_pull_requests'] + if _artifacts and self.request.POST.get('user_artifacts'): handle_artifacts = self.request.POST['user_artifacts'] old_values = c.user.get_api_data() try: - UserModel().delete(c.user, handle_repos=handle_repos, - handle_repo_groups=handle_repo_groups, - handle_user_groups=handle_user_groups, - handle_artifacts=handle_artifacts) + + UserModel().delete( + c.user, + handle_repos=handle_repos, + handle_repo_groups=handle_repo_groups, + handle_user_groups=handle_user_groups, + handle_pull_requests=handle_pull_requests, + handle_artifacts=handle_artifacts, + handle_new_owner=handle_user + ) audit_logger.store_web( 'user.delete', action_data={'old_data': old_values}, @@ -449,11 +476,13 @@ class UsersView(UserAppView): set_handle_flash_repos() set_handle_flash_repo_groups() set_handle_flash_user_groups() + set_handle_flash_pull_requests() set_handle_flash_artifacts() username = h.escape(old_values['username']) h.flash(_('Successfully deleted user `{}`').format(username), category='success') except (UserOwnsReposException, UserOwnsRepoGroupsException, - UserOwnsUserGroupsException, DefaultUserException) as e: + UserOwnsUserGroupsException, UserOwnsPullRequestsException, + UserOwnsArtifactsException, DefaultUserException) as e: h.flash(e, category='warning') except Exception: log.exception("Exception during deletion of user") @@ -502,6 +531,11 @@ class UsersView(UserAppView): user_id = self.db_user_id c.user = self.db_user + c.detach_user = User.get_first_super_admin() + detach_user_id = safe_int(self.request.GET.get('detach_user_id')) + if detach_user_id: + c.detach_user = User.get_or_404(detach_user_id) + c.active = 'advanced' c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id) c.personal_repo_group_name = RepoGroupModel()\ @@ -511,7 +545,6 @@ class UsersView(UserAppView): (x.user for x in c.user.user_review_rules), key=lambda u: u.username.lower()) - c.first_admin = User.get_first_super_admin() defaults = c.user.get_dict() # Interim workaround if the user participated on any pull requests as a @@ -705,6 +738,32 @@ class UsersView(UserAppView): @HasPermissionAllDecorator('hg.admin') @CSRFRequired() @view_config( + route_name='user_notice_dismiss', request_method='POST', + renderer='json_ext', xhr=True) + def user_notice_dismiss(self): + _ = self.request.translate + c = self.load_default_context() + + user_id = self.db_user_id + c.user = self.db_user + user_notice_id = safe_int(self.request.POST.get('notice_id')) + notice = UserNotice().query()\ + .filter(UserNotice.user_id == user_id)\ + .filter(UserNotice.user_notice_id == user_notice_id)\ + .scalar() + read = False + if notice: + notice.notice_read = True + Session().add(notice) + Session().commit() + read = True + + return {'notice': user_notice_id, 'read': read} + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @CSRFRequired() + @view_config( route_name='user_create_personal_repo_group', request_method='POST', renderer='rhodecode:templates/admin/users/user_edit.mako') def user_create_personal_repo_group(self): @@ -778,6 +837,25 @@ class UsersView(UserAppView): c.role_vcs = AuthTokenModel.cls.ROLE_VCS return self._get_template_context(c) + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='edit_user_auth_tokens_view', request_method='POST', + renderer='json_ext', xhr=True) + def auth_tokens_view(self): + _ = self.request.translate + c = self.load_default_context() + c.user = self.db_user + + auth_token_id = self.request.POST.get('auth_token_id') + + if auth_token_id: + token = UserApiKeys.get_or_404(auth_token_id) + + return { + 'auth_token': token.api_key + } + def maybe_attach_token_scope(self, token): # implemented in EE edition pass diff --git a/rhodecode/apps/channelstream/__init__.py b/rhodecode/apps/channelstream/__init__.py --- a/rhodecode/apps/channelstream/__init__.py +++ b/rhodecode/apps/channelstream/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/channelstream/views.py b/rhodecode/apps/channelstream/views.py --- a/rhodecode/apps/channelstream/views.py +++ b/rhodecode/apps/channelstream/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/debug_style/__init__.py b/rhodecode/apps/debug_style/__init__.py --- a/rhodecode/apps/debug_style/__init__.py +++ b/rhodecode/apps/debug_style/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/debug_style/views.py b/rhodecode/apps/debug_style/views.py --- a/rhodecode/apps/debug_style/views.py +++ b/rhodecode/apps/debug_style/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -88,6 +88,15 @@ Check if we should use full-topic or min 'modified': ['b/modified_file.rst'], 'removed': ['.idea'], }) + + exc_traceback = { + 'exc_utc_date': '2020-03-26T12:54:50.683281', + 'exc_id': 139638856342656, + 'exc_timestamp': '1585227290.683288', + 'version': 'v1', + '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', + 'exc_type': 'AttributeError' + } email_kwargs = { 'test': {}, 'message': { @@ -97,6 +106,13 @@ Check if we should use full-topic or min 'user': user, 'date': datetime.datetime.now(), }, + 'exception': { + 'email_prefix': '[RHODECODE ERROR]', + 'exc_id': exc_traceback['exc_id'], + 'exc_url': 'http://server-url/{}'.format(exc_traceback['exc_id']), + 'exc_type_name': 'NameError', + 'exc_traceback': exc_traceback, + }, 'password_reset': { 'password_reset_url': 'http://example.com/reset-rhodecode-password/token', @@ -204,7 +220,7 @@ def db(): 'pr_comment_url': 'http://comment-url', 'pr_comment_reply_url': 'http://comment-url#reply', - 'comment_file': 'rhodecode/model/db.py', + 'comment_file': 'rhodecode/model/get_flow_commits', 'comment_line': 'o1210', 'comment_type': 'todo', 'comment_body': ''' diff --git a/rhodecode/apps/file_store/__init__.py b/rhodecode/apps/file_store/__init__.py --- a/rhodecode/apps/file_store/__init__.py +++ b/rhodecode/apps/file_store/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/__init__.py b/rhodecode/apps/file_store/backends/__init__.py --- a/rhodecode/apps/file_store/backends/__init__.py +++ b/rhodecode/apps/file_store/backends/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/local_store.py b/rhodecode/apps/file_store/backends/local_store.py --- a/rhodecode/apps/file_store/backends/local_store.py +++ b/rhodecode/apps/file_store/backends/local_store.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/config_keys.py b/rhodecode/apps/file_store/config_keys.py --- a/rhodecode/apps/file_store/config_keys.py +++ b/rhodecode/apps/file_store/config_keys.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/exceptions.py b/rhodecode/apps/file_store/exceptions.py --- a/rhodecode/apps/file_store/exceptions.py +++ b/rhodecode/apps/file_store/exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/extensions.py b/rhodecode/apps/file_store/extensions.py --- a/rhodecode/apps/file_store/extensions.py +++ b/rhodecode/apps/file_store/extensions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/__init__.py b/rhodecode/apps/file_store/tests/__init__.py --- a/rhodecode/apps/file_store/tests/__init__.py +++ b/rhodecode/apps/file_store/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_upload_file.py b/rhodecode/apps/file_store/tests/test_upload_file.py --- a/rhodecode/apps/file_store/tests/test_upload_file.py +++ b/rhodecode/apps/file_store/tests/test_upload_file.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/utils.py b/rhodecode/apps/file_store/utils.py --- a/rhodecode/apps/file_store/utils.py +++ b/rhodecode/apps/file_store/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/views.py b/rhodecode/apps/file_store/views.py --- a/rhodecode/apps/file_store/views.py +++ b/rhodecode/apps/file_store/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/__init__.py b/rhodecode/apps/gist/__init__.py --- a/rhodecode/apps/gist/__init__.py +++ b/rhodecode/apps/gist/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/tests/__init__.py b/rhodecode/apps/gist/tests/__init__.py --- a/rhodecode/apps/gist/tests/__init__.py +++ b/rhodecode/apps/gist/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/tests/test_admin_gists.py b/rhodecode/apps/gist/tests/test_admin_gists.py --- a/rhodecode/apps/gist/tests/test_admin_gists.py +++ b/rhodecode/apps/gist/tests/test_admin_gists.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/views.py b/rhodecode/apps/gist/views.py --- a/rhodecode/apps/gist/views.py +++ b/rhodecode/apps/gist/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/__init__.py b/rhodecode/apps/home/__init__.py --- a/rhodecode/apps/home/__init__.py +++ b/rhodecode/apps/home/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/__init__.py b/rhodecode/apps/home/tests/__init__.py --- a/rhodecode/apps/home/tests/__init__.py +++ b/rhodecode/apps/home/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_goto_switched_data.py b/rhodecode/apps/home/tests/test_get_goto_switched_data.py --- a/rhodecode/apps/home/tests/test_get_goto_switched_data.py +++ b/rhodecode/apps/home/tests/test_get_goto_switched_data.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_repo_list_data.py b/rhodecode/apps/home/tests/test_get_repo_list_data.py --- a/rhodecode/apps/home/tests/test_get_repo_list_data.py +++ b/rhodecode/apps/home/tests/test_get_repo_list_data.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_user_data.py b/rhodecode/apps/home/tests/test_get_user_data.py --- a/rhodecode/apps/home/tests/test_get_user_data.py +++ b/rhodecode/apps/home/tests/test_get_user_data.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_user_group_data.py b/rhodecode/apps/home/tests/test_get_user_group_data.py --- a/rhodecode/apps/home/tests/test_get_user_group_data.py +++ b/rhodecode/apps/home/tests/test_get_user_group_data.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,7 +19,7 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_home.py b/rhodecode/apps/home/tests/test_home.py --- a/rhodecode/apps/home/tests/test_home.py +++ b/rhodecode/apps/home/tests/test_home.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/views.py b/rhodecode/apps/home/views.py --- a/rhodecode/apps/home/views.py +++ b/rhodecode/apps/home/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -35,8 +35,8 @@ from rhodecode.lib.index import searcher from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int from rhodecode.lib.vcs.nodes import FileNode from rhodecode.model.db import ( - func, true, or_, case, in_filter_generator, Session, - Repository, RepoGroup, User, UserGroup) + func, true, or_, case, cast, in_filter_generator, String, Session, + Repository, RepoGroup, User, UserGroup, PullRequest) from rhodecode.model.repo import RepoModel from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.user import UserModel @@ -111,7 +111,7 @@ class HomeView(BaseAppView, DataGridAppV org_query = name_contains allowed_ids = self._rhodecode_user.repo_acl_ids( ['repository.read', 'repository.write', 'repository.admin'], - cache=False, name_filter=name_contains) or [-1] + cache=True, name_filter=name_contains) or [-1] query = Session().query( Repository.repo_name, @@ -162,7 +162,7 @@ class HomeView(BaseAppView, DataGridAppV org_query = name_contains allowed_ids = self._rhodecode_user.repo_group_acl_ids( ['group.read', 'group.write', 'group.admin'], - cache=False, name_filter=name_contains) or [-1] + cache=True, name_filter=name_contains) or [-1] query = Session().query( RepoGroup.group_id, @@ -282,6 +282,61 @@ class HomeView(BaseAppView, DataGridAppV } for obj in acl_iter], True + def _get_pull_request_list(self, name_contains=None, limit=20): + org_query = name_contains + if not name_contains: + return [], False + + # TODO(marcink): should all logged in users be allowed to search others? + allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER + if not allowed_user_search: + return [], False + + name_contains = re.compile('(?:pr:[ ]?)(.+)').findall(name_contains) + if len(name_contains) != 1: + return [], False + + name_contains = name_contains[0] + + allowed_ids = self._rhodecode_user.repo_acl_ids( + ['repository.read', 'repository.write', 'repository.admin'], + cache=True) or [-1] + + query = Session().query( + PullRequest.pull_request_id, + PullRequest.title, + ) + query = query.join(Repository, Repository.repo_id == PullRequest.target_repo_id) + + query = query.filter(or_( + # generate multiple IN to fix limitation problems + *in_filter_generator(Repository.repo_id, allowed_ids) + )) + + query = query.order_by(PullRequest.pull_request_id) + + if name_contains: + ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) + query = query.filter(or_( + cast(PullRequest.pull_request_id, String).ilike(ilike_expression), + PullRequest.title.ilike(ilike_expression), + PullRequest.description.ilike(ilike_expression), + )) + + query = query.limit(limit) + + acl_iter = query + + return [ + { + 'id': obj.pull_request_id, + 'value': org_query, + 'value_display': 'pull request: `!{} - {}`'.format(obj.pull_request_id, obj.title[:50]), + 'type': 'pull_request', + 'url': h.route_path('pull_requests_global', pull_request_id=obj.pull_request_id) + } + for obj in acl_iter], True + def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None): repo_name = repo_group_name = None if repo: @@ -624,6 +679,17 @@ class HomeView(BaseAppView, DataGridAppV has_specialized_search = True res.append(no_match('No matching user groups found')) + # pr: type search + if not prefix_match: + pull_requests, prefix_match = self._get_pull_request_list(query) + if pull_requests: + has_specialized_search = True + for serialized_pull_request in pull_requests: + res.append(serialized_pull_request) + elif prefix_match: + has_specialized_search = True + res.append(no_match('No matching pull requests found')) + # FTS commit: type search if not prefix_match: commits, prefix_match = self._get_hash_commit_list( diff --git a/rhodecode/apps/hovercards/__init__.py b/rhodecode/apps/hovercards/__init__.py --- a/rhodecode/apps/hovercards/__init__.py +++ b/rhodecode/apps/hovercards/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2018-2019 RhodeCode GmbH +# Copyright (C) 2018-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/hovercards/views.py b/rhodecode/apps/hovercards/views.py --- a/rhodecode/apps/hovercards/views.py +++ b/rhodecode/apps/hovercards/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/__init__.py b/rhodecode/apps/journal/__init__.py --- a/rhodecode/apps/journal/__init__.py +++ b/rhodecode/apps/journal/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/tests/__init__.py b/rhodecode/apps/journal/tests/__init__.py --- a/rhodecode/apps/journal/tests/__init__.py +++ b/rhodecode/apps/journal/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/tests/test_journal.py b/rhodecode/apps/journal/tests/test_journal.py --- a/rhodecode/apps/journal/tests/test_journal.py +++ b/rhodecode/apps/journal/tests/test_journal.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/views.py b/rhodecode/apps/journal/views.py --- a/rhodecode/apps/journal/views.py +++ b/rhodecode/apps/journal/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/__init__.py b/rhodecode/apps/login/__init__.py --- a/rhodecode/apps/login/__init__.py +++ b/rhodecode/apps/login/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/tests/test_login.py b/rhodecode/apps/login/tests/test_login.py --- a/rhodecode/apps/login/tests/test_login.py +++ b/rhodecode/apps/login/tests/test_login.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/tests/test_password_reset.py b/rhodecode/apps/login/tests/test_password_reset.py --- a/rhodecode/apps/login/tests/test_password_reset.py +++ b/rhodecode/apps/login/tests/test_password_reset.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/tests/test_register_captcha.py b/rhodecode/apps/login/tests/test_register_captcha.py --- a/rhodecode/apps/login/tests/test_register_captcha.py +++ b/rhodecode/apps/login/tests/test_register_captcha.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/views.py b/rhodecode/apps/login/views.py --- a/rhodecode/apps/login/views.py +++ b/rhodecode/apps/login/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/__init__.py b/rhodecode/apps/my_account/__init__.py --- a/rhodecode/apps/my_account/__init__.py +++ b/rhodecode/apps/my_account/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -50,6 +50,9 @@ def includeme(config): name='my_account_auth_tokens', pattern=ADMIN_PREFIX + '/my_account/auth_tokens') config.add_route( + name='my_account_auth_tokens_view', + pattern=ADMIN_PREFIX + '/my_account/auth_tokens/view') + config.add_route( name='my_account_auth_tokens_add', pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new') config.add_route( diff --git a/rhodecode/apps/my_account/tests/__init__.py b/rhodecode/apps/my_account/tests/__init__.py --- a/rhodecode/apps/my_account/tests/__init__.py +++ b/rhodecode/apps/my_account/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py b/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py --- a/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py +++ b/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -49,7 +49,7 @@ class TestMyAccountAuthTokens(TestContro user = User.get(usr['user_id']) response = self.app.get(route_path('my_account_auth_tokens')) for token in user.auth_tokens: - response.mustcontain(token) + response.mustcontain(token[:4]) response.mustcontain('never') def test_my_account_add_auth_tokens_wrong_csrf(self, user_util): @@ -79,7 +79,7 @@ class TestMyAccountAuthTokens(TestContro response = response.follow() user = User.get(user_id) for auth_token in user.auth_tokens: - response.mustcontain(auth_token) + response.mustcontain(auth_token[:4]) def test_my_account_delete_auth_token(self, user_util): user = user_util.create_user(password='qweqwe') diff --git a/rhodecode/apps/my_account/tests/test_my_account_edit.py b/rhodecode/apps/my_account/tests/test_my_account_edit.py --- a/rhodecode/apps/my_account/tests/test_my_account_edit.py +++ b/rhodecode/apps/my_account/tests/test_my_account_edit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,7 +19,7 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_emails.py b/rhodecode/apps/my_account/tests/test_my_account_emails.py --- a/rhodecode/apps/my_account/tests/test_my_account_emails.py +++ b/rhodecode/apps/my_account/tests/test_my_account_emails.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_notifications.py b/rhodecode/apps/my_account/tests/test_my_account_notifications.py --- a/rhodecode/apps/my_account/tests/test_my_account_notifications.py +++ b/rhodecode/apps/my_account/tests/test_my_account_notifications.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_password.py b/rhodecode/apps/my_account/tests/test_my_account_password.py --- a/rhodecode/apps/my_account/tests/test_my_account_password.py +++ b/rhodecode/apps/my_account/tests/test_my_account_password.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_profile.py b/rhodecode/apps/my_account/tests/test_my_account_profile.py --- a/rhodecode/apps/my_account/tests/test_my_account_profile.py +++ b/rhodecode/apps/my_account/tests/test_my_account_profile.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_simple_views.py b/rhodecode/apps/my_account/tests/test_my_account_simple_views.py --- a/rhodecode/apps/my_account/tests/test_my_account_simple_views.py +++ b/rhodecode/apps/my_account/tests/test_my_account_simple_views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py b/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py --- a/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py +++ b/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/__init__.py b/rhodecode/apps/my_account/views/__init__.py --- a/rhodecode/apps/my_account/views/__init__.py +++ b/rhodecode/apps/my_account/views/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/my_account.py b/rhodecode/apps/my_account/views/my_account.py --- a/rhodecode/apps/my_account/views/my_account.py +++ b/rhodecode/apps/my_account/views/my_account.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -25,7 +25,7 @@ import string import formencode import formencode.htmlfill import peppercorn -from pyramid.httpexceptions import HTTPFound +from pyramid.httpexceptions import HTTPFound, HTTPNotFound from pyramid.view import view_config from rhodecode.apps._base import BaseAppView, DataGridAppView @@ -164,6 +164,27 @@ class MyAccountView(BaseAppView, DataGri c.role_vcs = AuthTokenModel.cls.ROLE_VCS return self._get_template_context(c) + @LoginRequired() + @NotAnonymous() + @CSRFRequired() + @view_config( + route_name='my_account_auth_tokens_view', request_method='POST', xhr=True, + renderer='json_ext') + def my_account_auth_tokens_view(self): + _ = self.request.translate + c = self.load_default_context() + + auth_token_id = self.request.POST.get('auth_token_id') + + if auth_token_id: + token = UserApiKeys.get_or_404(auth_token_id) + if token.user.user_id != c.user.user_id: + raise HTTPNotFound() + + return { + 'auth_token': token.api_key + } + def maybe_attach_token_scope(self, token): # implemented in EE edition pass @@ -702,12 +723,12 @@ class MyAccountView(BaseAppView, DataGri pull_requests = PullRequestModel().get_im_participating_in( user_id=self._rhodecode_user.user_id, - statuses=statuses, + statuses=statuses, query=search_q, offset=start, length=limit, order_by=order_by, order_dir=order_dir) pull_requests_total_count = PullRequestModel().count_im_participating_in( - user_id=self._rhodecode_user.user_id, statuses=statuses) + user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q) data = [] comments_model = CommentsModel() diff --git a/rhodecode/apps/my_account/views/my_account_notifications.py b/rhodecode/apps/my_account/views/my_account_notifications.py --- a/rhodecode/apps/my_account/views/my_account_notifications.py +++ b/rhodecode/apps/my_account/views/my_account_notifications.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/my_account_ssh_keys.py b/rhodecode/apps/my_account/views/my_account_ssh_keys.py --- a/rhodecode/apps/my_account/views/my_account_ssh_keys.py +++ b/rhodecode/apps/my_account/views/my_account_ssh_keys.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ops/__init__.py b/rhodecode/apps/ops/__init__.py --- a/rhodecode/apps/ops/__init__.py +++ b/rhodecode/apps/ops/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ops/views.py b/rhodecode/apps/ops/views.py --- a/rhodecode/apps/ops/views.py +++ b/rhodecode/apps/ops/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/__init__.py b/rhodecode/apps/repo_group/__init__.py --- a/rhodecode/apps/repo_group/__init__.py +++ b/rhodecode/apps/repo_group/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/__init__.py b/rhodecode/apps/repo_group/tests/__init__.py --- a/rhodecode/apps/repo_group/tests/__init__.py +++ b/rhodecode/apps/repo_group/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py b/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py --- a/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py +++ b/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py b/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py --- a/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py +++ b/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py b/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py --- a/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py +++ b/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/__init__.py b/rhodecode/apps/repo_group/views/__init__.py --- a/rhodecode/apps/repo_group/views/__init__.py +++ b/rhodecode/apps/repo_group/views/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/repo_group_advanced.py b/rhodecode/apps/repo_group/views/repo_group_advanced.py --- a/rhodecode/apps/repo_group/views/repo_group_advanced.py +++ b/rhodecode/apps/repo_group/views/repo_group_advanced.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/repo_group_permissions.py b/rhodecode/apps/repo_group/views/repo_group_permissions.py --- a/rhodecode/apps/repo_group/views/repo_group_permissions.py +++ b/rhodecode/apps/repo_group/views/repo_group_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/repo_group_settings.py b/rhodecode/apps/repo_group/views/repo_group_settings.py --- a/rhodecode/apps/repo_group/views/repo_group_settings.py +++ b/rhodecode/apps/repo_group/views/repo_group_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -156,6 +156,10 @@ def includeme(config): pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True) config.add_route( + name='repo_files_check_head', + pattern='/{repo_name:.*?[^/]}/check_head/{commit_id}/{f_path:.*}', + repo_route=True) + config.add_route( name='repo_files_remove_file', pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}', repo_route=True) diff --git a/rhodecode/apps/repository/tests/__init__.py b/rhodecode/apps/repository/tests/__init__.py --- a/rhodecode/apps/repository/tests/__init__.py +++ b/rhodecode/apps/repository/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_pull_requests_list.py b/rhodecode/apps/repository/tests/test_pull_requests_list.py --- a/rhodecode/apps/repository/tests/test_pull_requests_list.py +++ b/rhodecode/apps/repository/tests/test_pull_requests_list.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_bookmarks.py b/rhodecode/apps/repository/tests/test_repo_bookmarks.py --- a/rhodecode/apps/repository/tests/test_repo_bookmarks.py +++ b/rhodecode/apps/repository/tests/test_repo_bookmarks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_branches.py b/rhodecode/apps/repository/tests/test_repo_branches.py --- a/rhodecode/apps/repository/tests/test_repo_branches.py +++ b/rhodecode/apps/repository/tests/test_repo_branches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_changelog.py b/rhodecode/apps/repository/tests/test_repo_changelog.py --- a/rhodecode/apps/repository/tests/test_repo_changelog.py +++ b/rhodecode/apps/repository/tests/test_repo_changelog.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_commit_comments.py b/rhodecode/apps/repository/tests/test_repo_commit_comments.py --- a/rhodecode/apps/repository/tests/test_repo_commit_comments.py +++ b/rhodecode/apps/repository/tests/test_repo_commit_comments.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -22,8 +22,7 @@ import pytest from rhodecode.tests import TestController -from rhodecode.model.db import ( - ChangesetComment, Notification, UserNotification) +from rhodecode.model.db import ChangesetComment, Notification from rhodecode.model.meta import Session from rhodecode.lib import helpers as h @@ -269,7 +268,36 @@ class TestRepoCommitCommentsView(TestCon repo_name=backend.repo_name, commit_id=commit_id)) assert_comment_links(response, 0, 0) - @pytest.mark.parametrize('renderer, input, output', [ + def test_delete_forbidden_for_immutable_comments(self, backend): + self.log_user() + commit_id = backend.repo.get_commit('300').raw_id + text = u'CommentOnCommit' + + params = {'text': text, 'csrf_token': self.csrf_token} + self.app.post( + route_path( + 'repo_commit_comment_create', + repo_name=backend.repo_name, commit_id=commit_id), + params=params) + + comments = ChangesetComment.query().all() + assert len(comments) == 1 + comment_id = comments[0].comment_id + + comment = ChangesetComment.get(comment_id) + comment.immutable_state = ChangesetComment.OP_IMMUTABLE + Session().add(comment) + Session().commit() + + self.app.post( + route_path('repo_commit_comment_delete', + repo_name=backend.repo_name, + commit_id=commit_id, + comment_id=comment_id), + params={'csrf_token': self.csrf_token}, + status=403) + + @pytest.mark.parametrize('renderer, text_input, output', [ ('rst', 'plain text', '

plain text

'), ('rst', 'header\n======', '

header

'), ('rst', '*italics*', 'italics'), @@ -280,11 +308,11 @@ class TestRepoCommitCommentsView(TestCon ('markdown', '**bold**', 'bold'), ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain', 'md-header', 'md-italics', 'md-bold', ]) - def test_preview(self, renderer, input, output, backend, xhr_header): + def test_preview(self, renderer, text_input, output, backend, xhr_header): self.log_user() params = { 'renderer': renderer, - 'text': input, + 'text': text_input, 'csrf_token': self.csrf_token } commit_id = '0' * 16 # fake this for tests diff --git a/rhodecode/apps/repository/tests/test_repo_commits.py b/rhodecode/apps/repository/tests/test_repo_commits.py --- a/rhodecode/apps/repository/tests/test_repo_commits.py +++ b/rhodecode/apps/repository/tests/test_repo_commits.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_compare.py b/rhodecode/apps/repository/tests/test_repo_compare.py --- a/rhodecode/apps/repository/tests/test_repo_compare.py +++ b/rhodecode/apps/repository/tests/test_repo_compare.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -484,7 +484,7 @@ class TestCompareView(object): # outgoing commits between those commits compare_page = ComparePage(response) - compare_page.contains_commits(commits=[commit1], ancestors=[commit0]) + compare_page.contains_commits(commits=[commit1]) def test_errors_when_comparing_unknown_source_repo(self, backend): repo = backend.repo @@ -641,6 +641,7 @@ class ComparePage(AssertResponse): self.contains_one_link( 'r%s:%s' % (commit.idx, commit.short_id), self._commit_url(commit)) + if ancestors: response.mustcontain('Ancestor') for ancestor in ancestors: diff --git a/rhodecode/apps/repository/tests/test_repo_compare_local.py b/rhodecode/apps/repository/tests/test_repo_compare_local.py --- a/rhodecode/apps/repository/tests/test_repo_compare_local.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_local.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py --- a/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_feed.py b/rhodecode/apps/repository/tests/test_repo_feed.py --- a/rhodecode/apps/repository/tests/test_repo_feed.py +++ b/rhodecode/apps/repository/tests/test_repo_feed.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_files.py b/rhodecode/apps/repository/tests/test_repo_files.py --- a/rhodecode/apps/repository/tests/test_repo_files.py +++ b/rhodecode/apps/repository/tests/test_repo_files.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_forks.py b/rhodecode/apps/repository/tests/test_repo_forks.py --- a/rhodecode/apps/repository/tests/test_repo_forks.py +++ b/rhodecode/apps/repository/tests/test_repo_forks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_issue_tracker.py b/rhodecode/apps/repository/tests/test_repo_issue_tracker.py --- a/rhodecode/apps/repository/tests/test_repo_issue_tracker.py +++ b/rhodecode/apps/repository/tests/test_repo_issue_tracker.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_permissions.py b/rhodecode/apps/repository/tests/test_repo_permissions.py --- a/rhodecode/apps/repository/tests/test_repo_permissions.py +++ b/rhodecode/apps/repository/tests/test_repo_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_pullrequests.py b/rhodecode/apps/repository/tests/test_repo_pullrequests.py --- a/rhodecode/apps/repository/tests/test_repo_pullrequests.py +++ b/rhodecode/apps/repository/tests/test_repo_pullrequests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -629,7 +629,7 @@ class TestPullrequestsView(object): model_patcher = mock.patch.multiple( PullRequestModel, merge_repo=mock.Mock(return_value=merge_resp), - merge_status=mock.Mock(return_value=(True, 'WRONG_MESSAGE'))) + merge_status=mock.Mock(return_value=(None, True, 'WRONG_MESSAGE'))) with model_patcher: response = self.app.post( @@ -891,6 +891,8 @@ class TestPullrequestsView(object): vcs = repo.scm_instance() vcs.remove_ref('refs/heads/{}'.format(branch_name)) + # NOTE(marcink): run GC to ensure the commits are gone + vcs.run_gc() response = self.app.get(route_path( 'pullrequest_show', diff --git a/rhodecode/apps/repository/tests/test_repo_settings.py b/rhodecode/apps/repository/tests/test_repo_settings.py --- a/rhodecode/apps/repository/tests/test_repo_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py --- a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py +++ b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_summary.py b/rhodecode/apps/repository/tests/test_repo_summary.py --- a/rhodecode/apps/repository/tests/test_repo_summary.py +++ b/rhodecode/apps/repository/tests/test_repo_summary.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_tags.py b/rhodecode/apps/repository/tests/test_repo_tags.py --- a/rhodecode/apps/repository/tests/test_repo_tags.py +++ b/rhodecode/apps/repository/tests/test_repo_tags.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py --- a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_vcs_settings.py b/rhodecode/apps/repository/tests/test_vcs_settings.py --- a/rhodecode/apps/repository/tests/test_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_vcs_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/utils.py b/rhodecode/apps/repository/utils.py --- a/rhodecode/apps/repository/utils.py +++ b/rhodecode/apps/repository/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -20,6 +20,9 @@ from rhodecode.lib import helpers as h from rhodecode.lib.utils2 import safe_int +from rhodecode.model.pull_request import get_diff_info + +REVIEWER_API_VERSION = 'V3' def reviewer_as_json(user, reasons=None, mandatory=False, rules=None, user_group=None): @@ -47,15 +50,20 @@ def reviewer_as_json(user, reasons=None, def get_default_reviewers_data( current_user, source_repo, source_commit, target_repo, target_commit): + """ + Return json for default reviewers of a repository + """ - """ Return json for default reviewers of a repository """ + diff_info = get_diff_info( + source_repo, source_commit.raw_id, target_repo, target_commit.raw_id) reasons = ['Default reviewer', 'Repository owner'] json_reviewers = [reviewer_as_json( user=target_repo.user, reasons=reasons, mandatory=False, rules=None)] return { - 'api_ver': 'v1', # define version for later possible schema upgrade + 'api_ver': REVIEWER_API_VERSION, # define version for later possible schema upgrade + 'diff_info': diff_info, 'reviewers': json_reviewers, 'rules': {}, 'rules_data': {}, diff --git a/rhodecode/apps/repository/views/__init__.py b/rhodecode/apps/repository/views/__init__.py --- a/rhodecode/apps/repository/views/__init__.py +++ b/rhodecode/apps/repository/views/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_artifacts.py b/rhodecode/apps/repository/views/repo_artifacts.py --- a/rhodecode/apps/repository/views/repo_artifacts.py +++ b/rhodecode/apps/repository/views/repo_artifacts.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_audit_logs.py b/rhodecode/apps/repository/views/repo_audit_logs.py --- a/rhodecode/apps/repository/views/repo_audit_logs.py +++ b/rhodecode/apps/repository/views/repo_audit_logs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_automation.py b/rhodecode/apps/repository/views/repo_automation.py --- a/rhodecode/apps/repository/views/repo_automation.py +++ b/rhodecode/apps/repository/views/repo_automation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_bookmarks.py b/rhodecode/apps/repository/views/repo_bookmarks.py --- a/rhodecode/apps/repository/views/repo_bookmarks.py +++ b/rhodecode/apps/repository/views/repo_bookmarks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_branch_permissions.py b/rhodecode/apps/repository/views/repo_branch_permissions.py --- a/rhodecode/apps/repository/views/repo_branch_permissions.py +++ b/rhodecode/apps/repository/views/repo_branch_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_branches.py b/rhodecode/apps/repository/views/repo_branches.py --- a/rhodecode/apps/repository/views/repo_branches.py +++ b/rhodecode/apps/repository/views/repo_branches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_caches.py b/rhodecode/apps/repository/views/repo_caches.py --- a/rhodecode/apps/repository/views/repo_caches.py +++ b/rhodecode/apps/repository/views/repo_caches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_changelog.py b/rhodecode/apps/repository/views/repo_changelog.py --- a/rhodecode/apps/repository/views/repo_changelog.py +++ b/rhodecode/apps/repository/views/repo_changelog.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_checks.py b/rhodecode/apps/repository/views/repo_checks.py --- a/rhodecode/apps/repository/views/repo_checks.py +++ b/rhodecode/apps/repository/views/repo_checks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -22,7 +22,7 @@ import logging import collections -from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound +from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden from pyramid.view import view_config from pyramid.renderers import render from pyramid.response import Response @@ -380,6 +380,11 @@ class RepoCommitsView(RepoAppView): 'repo_commit', repo_name=self.db_repo_name, commit_id=current_id)) + commit = self.db_repo.get_commit(current_id) + CommentsModel().trigger_commit_comment_hook( + self.db_repo, self._rhodecode_user, 'create', + data={'comment': comment, 'commit': commit}) + # finalize, commit and redirect Session().commit() @@ -533,6 +538,10 @@ class RepoCommitsView(RepoAppView): # comment already deleted in another call probably return True + if comment.immutable: + # don't allow deleting comments that are immutable + raise HTTPForbidden() + is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name) super_admin = h.HasPermissionAny('hg.admin')() comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id) diff --git a/rhodecode/apps/repository/views/repo_compare.py b/rhodecode/apps/repository/views/repo_compare.py --- a/rhodecode/apps/repository/views/repo_compare.py +++ b/rhodecode/apps/repository/views/repo_compare.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_feed.py b/rhodecode/apps/repository/views/repo_feed.py --- a/rhodecode/apps/repository/views/repo_feed.py +++ b/rhodecode/apps/repository/views/repo_feed.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -245,7 +245,7 @@ class RepoFilesView(RepoAppView): return branch_name, sha_commit_id, is_head - def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False): + def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None): repo_id = self.db_repo.repo_id force_recache = self.get_recache_flag() @@ -261,19 +261,19 @@ class RepoFilesView(RepoAppView): cache_namespace_uid = 'cache_repo.{}'.format(repo_id) region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid) - @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, - condition=cache_on) - def compute_file_tree(ver, repo_id, commit_id, f_path, full_load): + @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on) + def compute_file_tree(ver, _name_hash, _repo_id, _commit_id, _f_path, _full_load, _at_rev): log.debug('Generating cached file tree at ver:%s for repo_id: %s, %s, %s', - ver, repo_id, commit_id, f_path) + ver, _repo_id, _commit_id, _f_path) - c.full_load = full_load + c.full_load = _full_load return render( 'rhodecode:templates/files/files_browser_tree.mako', - self._get_template_context(c), self.request) + self._get_template_context(c), self.request, _at_rev) return compute_file_tree( - rc_cache.FILE_TREE_CACHE_VER, self.db_repo.repo_id, commit_id, f_path, full_load) + rc_cache.FILE_TREE_CACHE_VER, self.db_repo.repo_name_hash, + self.db_repo.repo_id, commit_id, f_path, full_load, at_rev) def _get_archive_spec(self, fname): log.debug('Detecting archive spec for: `%s`', fname) @@ -617,15 +617,12 @@ class RepoFilesView(RepoAppView): c.renderer = view_name == 'repo_files:rendered' or \ not self.request.GET.get('no-render', False) - # redirect to given commit_id from form if given - get_commit_id = self.request.GET.get('at_rev', None) - if get_commit_id: - self._get_commit_or_redirect(get_commit_id) + commit_id, f_path = self._get_commit_and_path() - commit_id, f_path = self._get_commit_and_path() c.commit = self._get_commit_or_redirect(commit_id) c.branch = self.request.GET.get('branch', None) c.f_path = f_path + at_rev = self.request.GET.get('at') # prev link try: @@ -705,7 +702,7 @@ class RepoFilesView(RepoAppView): c.authors = [] # this loads a simple tree without metadata to speed things up # later via ajax we call repo_nodetree_full and fetch whole - c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path) + c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev) c.readme_data, c.readme_file = \ self._get_readme_data(self.db_repo, c.visual.default_renderer, @@ -782,9 +779,10 @@ class RepoFilesView(RepoAppView): c.file = dir_node c.commit = commit + at_rev = self.request.GET.get('at') html = self._get_tree_at_commit( - c, commit.raw_id, dir_node.path, full_load=True) + c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev) return Response(html) @@ -915,13 +913,12 @@ class RepoFilesView(RepoAppView): cache_namespace_uid = 'cache_repo.{}'.format(repo_id) region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid) - @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, - condition=cache_on) - def compute_file_search(repo_id, commit_id, f_path): + @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on) + def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path): log.debug('Generating cached nodelist for repo_id:%s, %s, %s', - repo_id, commit_id, f_path) + _repo_id, commit_id, f_path) try: - _d, _f = ScmModel().get_quick_filter_nodes(repo_name, commit_id, f_path) + _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path) except (RepositoryError, CommitDoesNotExistError, Exception) as e: log.exception(safe_str(e)) h.flash(safe_str(h.escape(e)), category='error') @@ -931,7 +928,8 @@ class RepoFilesView(RepoAppView): return _d + _f - result = compute_file_search(self.db_repo.repo_id, commit_id, f_path) + result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, + commit_id, f_path) return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result) @LoginRequired() @@ -961,6 +959,9 @@ class RepoFilesView(RepoAppView): return commit_id def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type): + return commit_id + + # NOTE(dan): old code we used in "diff" mode compare new_f_path = vcspath.join(name, f_path) return u'%s@%s' % (new_f_path, commit_id) @@ -1035,10 +1036,24 @@ class RepoFilesView(RepoAppView): file_history, _hist = self._get_node_history(commit, f_path) res = [] - for obj in file_history: + for section_items, section in file_history: + items = [] + for obj_id, obj_text, obj_type in section_items: + at_rev = '' + if obj_type in ['branch', 'bookmark', 'tag']: + at_rev = obj_text + entry = { + 'id': obj_id, + 'text': obj_text, + 'type': obj_type, + 'at_rev': at_rev + } + + items.append(entry) + res.append({ - 'text': obj[1], - 'children': [{'id': o[0], 'text': o[1], 'type': o[2]} for o in obj[0]] + 'text': section, + 'children': items }) data = { @@ -1098,6 +1113,42 @@ class RepoFilesView(RepoAppView): @LoginRequired() @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') @view_config( + route_name='repo_files_check_head', request_method='POST', + renderer='json_ext', xhr=True) + def repo_files_check_head(self): + self.load_default_context() + + commit_id, f_path = self._get_commit_and_path() + _branch_name, _sha_commit_id, is_head = \ + self._is_valid_head(commit_id, self.rhodecode_vcs_repo) + + new_path = self.request.POST.get('path') + operation = self.request.POST.get('operation') + path_exist = '' + + if new_path and operation in ['create', 'upload']: + new_f_path = os.path.join(f_path.lstrip('/'), new_path) + try: + commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id) + # NOTE(dan): construct whole path without leading / + file_node = commit_obj.get_node(new_f_path) + if file_node is not None: + path_exist = new_f_path + except EmptyRepositoryError: + pass + except Exception: + pass + + return { + 'branch': _branch_name, + 'sha': _sha_commit_id, + 'is_head': is_head, + 'path_exists': path_exist + } + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @view_config( route_name='repo_files_remove_file', request_method='GET', renderer='rhodecode:templates/files/files_delete.mako') def repo_files_remove_file(self): diff --git a/rhodecode/apps/repository/views/repo_forks.py b/rhodecode/apps/repository/views/repo_forks.py --- a/rhodecode/apps/repository/views/repo_forks.py +++ b/rhodecode/apps/repository/views/repo_forks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_maintainance.py b/rhodecode/apps/repository/views/repo_maintainance.py --- a/rhodecode/apps/repository/views/repo_maintainance.py +++ b/rhodecode/apps/repository/views/repo_maintainance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_permissions.py b/rhodecode/apps/repository/views/repo_permissions.py --- a/rhodecode/apps/repository/views/repo_permissions.py +++ b/rhodecode/apps/repository/views/repo_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -114,21 +114,26 @@ class RepoSettingsPermissionsView(RepoAp private_flag = str2bool(self.request.POST.get('private')) try: - RepoModel().update( - self.db_repo, **{'repo_private': private_flag, 'repo_name': self.db_repo_name}) + repo = RepoModel().get(self.db_repo.repo_id) + repo.private = private_flag + Session().add(repo) + RepoModel().grant_user_permission( + repo=self.db_repo, user=User.DEFAULT_USER, perm='repository.none' + ) + Session().commit() h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name), category='success') + # NOTE(dan): we change repo private mode we need to notify all USERS + affected_user_ids = User.get_all_user_ids() + PermissionModel().trigger_permission_flush(affected_user_ids) + except Exception: log.exception("Exception during update of repository") h.flash(_('Error occurred during update of repository {}').format( self.db_repo_name), category='error') - # NOTE(dan): we change repo private mode we need to notify all USERS - affected_user_ids = User.get_all_user_ids() - PermissionModel().trigger_permission_flush(affected_user_ids) - return { 'redirect_url': h.route_path('edit_repo_perms', repo_name=self.db_repo_name), 'private': private_flag diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -40,12 +40,12 @@ from rhodecode.lib.auth import ( NotAnonymous, CSRFRequired) from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason -from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError, - RepositoryRequirementError, EmptyRepositoryError) +from rhodecode.lib.vcs.exceptions import ( + CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError) from rhodecode.model.changeset_status import ChangesetStatusModel from rhodecode.model.comment import CommentsModel -from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion, - ChangesetComment, ChangesetStatus, Repository) +from rhodecode.model.db import ( + func, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository) from rhodecode.model.forms import PullRequestForm from rhodecode.model.meta import Session from rhodecode.model.pull_request import PullRequestModel, MergeCheck @@ -210,10 +210,12 @@ class RepoPullRequestsView(RepoAppView, return caching_enabled def _get_diffset(self, source_repo_name, source_repo, + ancestor_commit, source_ref_id, target_ref_id, target_commit, source_commit, diff_limit, file_limit, fulldiff, hide_whitespace_changes, diff_context): + target_ref_id = ancestor_commit.raw_id vcs_diff = PullRequestModel().get_diff( source_repo, source_ref_id, target_ref_id, hide_whitespace_changes, diff_context) @@ -278,6 +280,7 @@ class RepoPullRequestsView(RepoAppView, _new_state = { 'created': PullRequest.STATE_CREATED, }.get(self.request.GET.get('force_state')) + if c.is_super_admin and _new_state: with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state): h.flash( @@ -396,9 +399,12 @@ class RepoPullRequestsView(RepoAppView, pull_request_latest, auth_user=self._rhodecode_user, translator=self.request.translate, force_shadow_repo_refresh=force_refresh) + c.pr_merge_errors = _merge_check.error_details c.pr_merge_possible = not _merge_check.failed c.pr_merge_message = _merge_check.merge_msg + c.pr_merge_source_commit = _merge_check.source_commit + c.pr_merge_target_commit = _merge_check.target_commit c.pr_merge_info = MergeCheck.get_merge_conditions( pull_request_latest, translator=self.request.translate) @@ -537,6 +543,13 @@ class RepoPullRequestsView(RepoAppView, (ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit) = cached_diff['commits'] else: + # NOTE(marcink): we reach potentially unreachable errors when a PR has + # merge errors resulting in potentially hidden commits in the shadow repo. + maybe_unreachable = _merge_check.MERGE_CHECK in _merge_check.error_details \ + and _merge_check.merge_response + maybe_unreachable = maybe_unreachable \ + and _merge_check.merge_response.metadata.get('unresolved_files') + log.debug("Using unreachable commits due to MERGE_CHECK in merge simulation") diff_commit_cache = \ (ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit) = self.get_commits( @@ -547,7 +560,8 @@ class RepoPullRequestsView(RepoAppView, source_scm, target_commit, target_ref_id, - target_scm) + target_scm, + maybe_unreachable=maybe_unreachable) # register our commit range for comm in commit_cache.values(): @@ -581,11 +595,10 @@ class RepoPullRequestsView(RepoAppView, has_proper_diff_cache = cached_diff and cached_diff.get('commits') if not force_recache and has_proper_diff_cache: c.diffset = cached_diff['diff'] - (ancestor_commit, commit_cache, missing_requirements, - source_commit, target_commit) = cached_diff['commits'] else: c.diffset = self._get_diffset( c.source_repo.repo_name, commits_source_repo, + c.ancestor_commit, source_ref_id, target_ref_id, target_commit, source_commit, diff_limit, file_limit, c.fulldiff, @@ -665,8 +678,10 @@ class RepoPullRequestsView(RepoAppView, # calculate the diff for commits between versions c.commit_changes = [] - mark = lambda cs, fw: list( - h.itertools.izip_longest([], cs, fillvalue=fw)) + + def mark(cs, fw): + return list(h.itertools.izip_longest([], cs, fillvalue=fw)) + for c_type, raw_id in mark(commit_changes.added, 'a') \ + mark(commit_changes.removed, 'r') \ + mark(commit_changes.common, 'c'): @@ -698,15 +713,22 @@ class RepoPullRequestsView(RepoAppView, def get_commits( self, commits_source_repo, pull_request_at_ver, source_commit, - source_ref_id, source_scm, target_commit, target_ref_id, target_scm): + source_ref_id, source_scm, target_commit, target_ref_id, target_scm, + maybe_unreachable=False): + commit_cache = collections.OrderedDict() missing_requirements = False + try: pre_load = ["author", "date", "message", "branch", "parents"] - show_revs = pull_request_at_ver.revisions - for rev in show_revs: - comm = commits_source_repo.get_commit( - commit_id=rev, pre_load=pre_load) + + pull_request_commits = pull_request_at_ver.revisions + log.debug('Loading %s commits from %s', + len(pull_request_commits), commits_source_repo) + + for rev in pull_request_commits: + comm = commits_source_repo.get_commit(commit_id=rev, pre_load=pre_load, + maybe_unreachable=maybe_unreachable) commit_cache[comm.raw_id] = comm # Order here matters, we first need to get target, and then @@ -715,22 +737,21 @@ class RepoPullRequestsView(RepoAppView, commit_id=safe_str(target_ref_id)) source_commit = commits_source_repo.get_commit( - commit_id=safe_str(source_ref_id)) + commit_id=safe_str(source_ref_id), maybe_unreachable=True) except CommitDoesNotExistError: - log.warning( - 'Failed to get commit from `{}` repo'.format( - commits_source_repo), exc_info=True) + log.warning('Failed to get commit from `{}` repo'.format( + commits_source_repo), exc_info=True) except RepositoryRequirementError: - log.warning( - 'Failed to get all required data from repo', exc_info=True) + log.warning('Failed to get all required data from repo', exc_info=True) missing_requirements = True - ancestor_commit = None + + pr_ancestor_id = pull_request_at_ver.common_ancestor_id + try: - ancestor_id = source_scm.get_common_ancestor( - source_commit.raw_id, target_commit.raw_id, target_scm) - ancestor_commit = source_scm.get_commit(ancestor_id) + ancestor_commit = source_scm.get_commit(pr_ancestor_id) except Exception: ancestor_commit = None + return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit def assure_not_empty_repo(self): @@ -934,6 +955,7 @@ class RepoPullRequestsView(RepoAppView, target_repo = _form['target_repo'] target_ref = _form['target_ref'] commit_ids = _form['revisions'][::-1] + common_ancestor_id = _form['common_ancestor'] # find the ancestor for this pr source_db_repo = Repository.get_by_repo_name(_form['source_repo']) @@ -1020,6 +1042,7 @@ class RepoPullRequestsView(RepoAppView, target_repo=target_repo, target_ref=target_ref, revisions=commit_ids, + common_ancestor_id=common_ancestor_id, reviewers=reviewers, title=pullrequest_title, description=description, @@ -1458,6 +1481,10 @@ class RepoPullRequestsView(RepoAppView, self.request.matchdict['comment_id']) comment_id = comment.comment_id + if comment.immutable: + # don't allow deleting comments that are immutable + raise HTTPForbidden() + if pull_request.is_closed(): log.debug('comment: forbidden because pull request is closed') raise HTTPForbidden() diff --git a/rhodecode/apps/repository/views/repo_review_rules.py b/rhodecode/apps/repository/views/repo_review_rules.py --- a/rhodecode/apps/repository/views/repo_review_rules.py +++ b/rhodecode/apps/repository/views/repo_review_rules.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -54,8 +54,20 @@ class RepoReviewRulesView(RepoAppView): renderer='json_ext') def repo_default_reviewers_data(self): self.load_default_context() - target_repo_name = self.request.GET.get('target_repo', self.db_repo.repo_name) + + request = self.request + source_repo = self.db_repo + source_repo_name = source_repo.repo_name + target_repo_name = request.GET.get('target_repo', source_repo_name) target_repo = Repository.get_by_repo_name(target_repo_name) + + source_ref = request.GET['source_ref'] + target_ref = request.GET['target_ref'] + source_commit = source_repo.get_commit(source_ref) + target_commit = target_repo.get_commit(target_ref) + + current_user = request.user.get_instance() review_data = get_default_reviewers_data( - self.db_repo.user, None, None, target_repo, None) + current_user, source_repo, source_commit, target_repo, target_commit) + return review_data diff --git a/rhodecode/apps/repository/views/repo_settings.py b/rhodecode/apps/repository/views/repo_settings.py --- a/rhodecode/apps/repository/views/repo_settings.py +++ b/rhodecode/apps/repository/views/repo_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_advanced.py b/rhodecode/apps/repository/views/repo_settings_advanced.py --- a/rhodecode/apps/repository/views/repo_settings_advanced.py +++ b/rhodecode/apps/repository/views/repo_settings_advanced.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -22,6 +22,7 @@ import logging from pyramid.view import view_config from pyramid.httpexceptions import HTTPFound +from packaging.version import Version from rhodecode import events from rhodecode.apps._base import RepoAppView @@ -64,12 +65,19 @@ class RepoSettingsView(RepoAppView): c = self.load_default_context() c.active = 'advanced' - c.default_user_id = User.get_default_user().user_id + c.default_user_id = User.get_default_user_id() c.in_public_journal = UserFollowing.query() \ .filter(UserFollowing.user_id == c.default_user_id) \ .filter(UserFollowing.follows_repository == self.db_repo).scalar() c.ver_info_dict = self.rhodecode_vcs_repo.get_hooks_info() + c.hooks_outdated = False + + try: + if Version(c.ver_info_dict['pre_version']) < Version(c.rhodecode_version): + c.hooks_outdated = True + except Exception: + pass # update commit cache if GET flag is present if self.request.GET.get('update_commit_cache'): @@ -212,7 +220,7 @@ class RepoSettingsView(RepoAppView): _ = self.request.translate try: - user_id = User.get_default_user().user_id + user_id = User.get_default_user_id() ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id) h.flash(_('Updated repository visibility in public journal'), category='success') diff --git a/rhodecode/apps/repository/views/repo_settings_fields.py b/rhodecode/apps/repository/views/repo_settings_fields.py --- a/rhodecode/apps/repository/views/repo_settings_fields.py +++ b/rhodecode/apps/repository/views/repo_settings_fields.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_issue_trackers.py b/rhodecode/apps/repository/views/repo_settings_issue_trackers.py --- a/rhodecode/apps/repository/views/repo_settings_issue_trackers.py +++ b/rhodecode/apps/repository/views/repo_settings_issue_trackers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_remote.py b/rhodecode/apps/repository/views/repo_settings_remote.py --- a/rhodecode/apps/repository/views/repo_settings_remote.py +++ b/rhodecode/apps/repository/views/repo_settings_remote.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_vcs.py b/rhodecode/apps/repository/views/repo_settings_vcs.py --- a/rhodecode/apps/repository/views/repo_settings_vcs.py +++ b/rhodecode/apps/repository/views/repo_settings_vcs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_strip.py b/rhodecode/apps/repository/views/repo_strip.py --- a/rhodecode/apps/repository/views/repo_strip.py +++ b/rhodecode/apps/repository/views/repo_strip.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_summary.py b/rhodecode/apps/repository/views/repo_summary.py --- a/rhodecode/apps/repository/views/repo_summary.py +++ b/rhodecode/apps/repository/views/repo_summary.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_tags.py b/rhodecode/apps/repository/views/repo_tags.py --- a/rhodecode/apps/repository/views/repo_tags.py +++ b/rhodecode/apps/repository/views/repo_tags.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/search/__init__.py b/rhodecode/apps/search/__init__.py --- a/rhodecode/apps/search/__init__.py +++ b/rhodecode/apps/search/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/search/tests/test_search.py b/rhodecode/apps/search/tests/test_search.py --- a/rhodecode/apps/search/tests/test_search.py +++ b/rhodecode/apps/search/tests/test_search.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/search/views.py b/rhodecode/apps/search/views.py --- a/rhodecode/apps/search/views.py +++ b/rhodecode/apps/search/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -60,11 +60,10 @@ def perform_search(request, tmpl_context errors = e.children def url_generator(page_num): - q = urllib.quote(safe_str(search_query)) query_params = { 'page': page_num, - 'q': q, + 'q': safe_str(search_query), 'type': safe_str(search_type), 'max_lines': search_max_lines, 'sort': search_sort @@ -72,7 +71,6 @@ def perform_search(request, tmpl_context return '?' + urllib.urlencode(query_params) - c = tmpl_context search_query = search_params.get('search_query') search_type = search_params.get('search_type') diff --git a/rhodecode/apps/ssh_support/__init__.py b/rhodecode/apps/ssh_support/__init__.py --- a/rhodecode/apps/ssh_support/__init__.py +++ b/rhodecode/apps/ssh_support/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/config_keys.py b/rhodecode/apps/ssh_support/config_keys.py --- a/rhodecode/apps/ssh_support/config_keys.py +++ b/rhodecode/apps/ssh_support/config_keys.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/events.py b/rhodecode/apps/ssh_support/events.py --- a/rhodecode/apps/ssh_support/events.py +++ b/rhodecode/apps/ssh_support/events.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/__init__.py b/rhodecode/apps/ssh_support/lib/__init__.py --- a/rhodecode/apps/ssh_support/lib/__init__.py +++ b/rhodecode/apps/ssh_support/lib/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/__init__.py b/rhodecode/apps/ssh_support/lib/backends/__init__.py --- a/rhodecode/apps/ssh_support/lib/backends/__init__.py +++ b/rhodecode/apps/ssh_support/lib/backends/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -91,7 +91,6 @@ class SshWrapper(object): def get_repo_details(self, mode): vcs_type = mode if mode in ['svn', 'hg', 'git'] else None - mode = mode repo_name = None hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$' @@ -101,8 +100,7 @@ class SshWrapper(object): repo_name = hg_match.group(1).strip('/') return vcs_type, repo_name, mode - git_pattern = ( - r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$') + git_pattern = r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$' git_match = re.match(git_pattern, self.command) if git_match is not None: vcs_type = 'git' @@ -115,7 +113,8 @@ class SshWrapper(object): if svn_match is not None: vcs_type = 'svn' - # Repo name should be extracted from the input stream + # Repo name should be extracted from the input stream, we're unable to + # extract it at this point in execution return vcs_type, repo_name, mode return vcs_type, repo_name, mode @@ -188,8 +187,7 @@ class SshWrapper(object): log.debug('SSH Connection info %s', self.get_connection_info()) if shell and self.command is None: - log.info( - 'Dropping to shell, no command given and shell is allowed') + log.info('Dropping to shell, no command given and shell is allowed') os.execl('/bin/bash', '-l') exit_code = 1 @@ -216,8 +214,7 @@ class SshWrapper(object): exit_code = -1 else: - log.error( - 'Unhandled Command: "%s" Aborting.', self.command) + log.error('Unhandled Command: "%s" Aborting.', self.command) exit_code = -1 return exit_code diff --git a/rhodecode/apps/ssh_support/lib/backends/base.py b/rhodecode/apps/ssh_support/lib/backends/base.py --- a/rhodecode/apps/ssh_support/lib/backends/base.py +++ b/rhodecode/apps/ssh_support/lib/backends/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -66,9 +66,8 @@ class VcsServer(object): def _check_permissions(self, action): permission = self.user_permissions.get(self.repo_name) - log.debug( - 'permission for %s on %s are: %s', - self.user, self.repo_name, permission) + log.debug('permission for %s on %s are: %s', + self.user, self.repo_name, permission) if not permission: log.error('user `%s` permissions to repo:%s are empty. Forbidding access.', diff --git a/rhodecode/apps/ssh_support/lib/backends/git.py b/rhodecode/apps/ssh_support/lib/backends/git.py --- a/rhodecode/apps/ssh_support/lib/backends/git.py +++ b/rhodecode/apps/ssh_support/lib/backends/git.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -68,8 +68,7 @@ class GitServer(VcsServer): self.store = store self.ini_path = ini_path self.repo_name = repo_name - self._path = self.git_path = config.get( - 'app:main', 'ssh.executable.git') + self._path = self.git_path = config.get('app:main', 'ssh.executable.git') self.repo_mode = repo_mode self.tunnel = GitTunnelWrapper(server=self) diff --git a/rhodecode/apps/ssh_support/lib/backends/hg.py b/rhodecode/apps/ssh_support/lib/backends/hg.py --- a/rhodecode/apps/ssh_support/lib/backends/hg.py +++ b/rhodecode/apps/ssh_support/lib/backends/hg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/svn.py b/rhodecode/apps/ssh_support/lib/backends/svn.py --- a/rhodecode/apps/ssh_support/lib/backends/svn.py +++ b/rhodecode/apps/ssh_support/lib/backends/svn.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -67,9 +67,12 @@ class SubversionTunnelWrapper(object): def command(self): root = self.server.get_root_store() + username = self.server.user.username + command = [ self.server.svn_path, '-t', '--config-file', self.svn_conf_path, + '--tunnel-user', username, '-r', root] log.debug("Final CMD: %s", ' '.join(command)) return command @@ -95,9 +98,8 @@ class SubversionTunnelWrapper(object): signal.alarm(self.timeout) first_response = self._read_first_client_response() signal.alarm(0) - return ( - self._parse_first_client_response(first_response) - if first_response else None) + return (self._parse_first_client_response(first_response) + if first_response else None) def patch_first_client_response(self, response, **kwargs): self.create_hooks_env() @@ -112,9 +114,8 @@ class SubversionTunnelWrapper(object): self.process.stdin.write(buffer_) def fail(self, message): - print( - "( failure ( ( 210005 {message} 0: 0 ) ) )".format( - message=self._svn_string(message))) + print("( failure ( ( 210005 {message} 0: 0 ) ) )".format( + message=self._svn_string(message))) self.remove_configs() self.process.kill() return 1 @@ -139,6 +140,7 @@ class SubversionTunnelWrapper(object): brackets_stack.pop() elif next_byte == " " and not brackets_stack: break + return buffer_ def _parse_first_client_response(self, buffer_): @@ -149,8 +151,7 @@ class SubversionTunnelWrapper(object): ( version:number ( cap:word ... ) url:string ? ra-client:string ( ? client:string ) ) - Please check https://svn.apache.org/repos/asf/subversion/trunk/ - subversion/libsvn_ra_svn/protocol + Please check https://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_ra_svn/protocol """ version_re = r'(?P\d+)' capabilities_re = r'\(\s(?P[\w\d\-\ ]+)\s\)' @@ -163,8 +164,35 @@ class SubversionTunnelWrapper(object): version=version_re, capabilities=capabilities_re, url=url_re, ra_client=ra_client_re, client=client_re)) matcher = regex.match(buffer_) + return matcher.groupdict() if matcher else None + def _match_repo_name(self, url): + """ + Given an server url, try to match it against ALL known repository names. + This handles a tricky SVN case for SSH and subdir commits. + E.g if our repo name is my-svn-repo, a svn commit on file in a subdir would + result in the url with this subdir added. + """ + # case 1 direct match, we don't do any "heavy" lookups + if url in self.server.user_permissions: + return url + + log.debug('Extracting repository name from subdir path %s', url) + # case 2 we check all permissions, and match closes possible case... + # NOTE(dan): In this case we only know that url has a subdir parts, it's safe + # to assume that it will have the repo name as prefix, we ensure the prefix + # for similar repositories isn't matched by adding a / + # e.g subgroup/repo-name/ and subgroup/repo-name-1/ would work correct. + for repo_name in self.server.user_permissions: + repo_name_prefix = repo_name + '/' + if url.startswith(repo_name_prefix): + log.debug('Found prefix %s match, returning proper repository name', + repo_name_prefix) + return repo_name + + return + def run(self, extras): action = 'pull' self.create_svn_config() @@ -175,7 +203,8 @@ class SubversionTunnelWrapper(object): return self.fail("Repository name cannot be extracted") url_parts = urlparse.urlparse(first_response['url']) - self.server.repo_name = url_parts.path.strip('/') + + self.server.repo_name = self._match_repo_name(url_parts.path.strip('/')) exit_code = self.server._check_permissions(action) if exit_code: @@ -200,10 +229,10 @@ class SubversionServer(VcsServer): .__init__(user, user_permissions, config, env) self.store = store self.ini_path = ini_path - # this is set in .run() from input stream + # NOTE(dan): repo_name at this point is empty, + # this is set later in .run() based from parsed input stream self.repo_name = repo_name - self._path = self.svn_path = config.get( - 'app:main', 'ssh.executable.svn') + self._path = self.svn_path = config.get('app:main', 'ssh.executable.svn') self.tunnel = SubversionTunnelWrapper(server=self) diff --git a/rhodecode/apps/ssh_support/lib/ssh_wrapper.py b/rhodecode/apps/ssh_support/lib/ssh_wrapper.py --- a/rhodecode/apps/ssh_support/lib/ssh_wrapper.py +++ b/rhodecode/apps/ssh_support/lib/ssh_wrapper.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/subscribers.py b/rhodecode/apps/ssh_support/subscribers.py --- a/rhodecode/apps/ssh_support/subscribers.py +++ b/rhodecode/apps/ssh_support/subscribers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/__init__.py b/rhodecode/apps/ssh_support/tests/__init__.py --- a/rhodecode/apps/ssh_support/tests/__init__.py +++ b/rhodecode/apps/ssh_support/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/conftest.py b/rhodecode/apps/ssh_support/tests/conftest.py --- a/rhodecode/apps/ssh_support/tests/conftest.py +++ b/rhodecode/apps/ssh_support/tests/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_server_git.py b/rhodecode/apps/ssh_support/tests/test_server_git.py --- a/rhodecode/apps/ssh_support/tests/test_server_git.py +++ b/rhodecode/apps/ssh_support/tests/test_server_git.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_server_hg.py b/rhodecode/apps/ssh_support/tests/test_server_hg.py --- a/rhodecode/apps/ssh_support/tests/test_server_hg.py +++ b/rhodecode/apps/ssh_support/tests/test_server_hg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_server_svn.py b/rhodecode/apps/ssh_support/tests/test_server_svn.py --- a/rhodecode/apps/ssh_support/tests/test_server_svn.py +++ b/rhodecode/apps/ssh_support/tests/test_server_svn.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -70,8 +70,10 @@ class TestSubversionServer(object): def test_command(self, svn_server): server = svn_server.create() expected_command = [ - svn_server.svn_path, '-t', '--config-file', - server.tunnel.svn_conf_path, '-r', svn_server.root + svn_server.svn_path, '-t', + '--config-file', server.tunnel.svn_conf_path, + '--tunnel-user', svn_server.user.username, + '-r', svn_server.root ] assert expected_command == server.tunnel.command() @@ -89,6 +91,86 @@ class TestSubversionServer(object): result = server._check_permissions(action) assert result is code + @pytest.mark.parametrize('permissions, access_paths, expected_match', [ + # not matched repository name + ({ + 'test-svn': '' + }, ['test-svn-1', 'test-svn-1/subpath'], + None), + + # exact match + ({ + 'test-svn': '' + }, + ['test-svn'], + 'test-svn'), + + # subdir commits + ({ + 'test-svn': '' + }, + ['test-svn/foo', + 'test-svn/foo/test-svn', + 'test-svn/trunk/development.txt', + ], + 'test-svn'), + + # subgroups + similar patterns + ({ + 'test-svn': '', + 'test-svn-1': '', + 'test-svn-subgroup/test-svn': '', + + }, + ['test-svn-1', + 'test-svn-1/foo/test-svn', + 'test-svn-1/test-svn', + ], + 'test-svn-1'), + + # subgroups + similar patterns + ({ + 'test-svn-1': '', + 'test-svn-10': '', + 'test-svn-100': '', + }, + ['test-svn-10', + 'test-svn-10/foo/test-svn', + 'test-svn-10/test-svn', + ], + 'test-svn-10'), + + # subgroups + similar patterns + ({ + 'name': '', + 'nameContains': '', + 'nameContainsThis': '', + }, + ['nameContains', + 'nameContains/This', + 'nameContains/This/test-svn', + ], + 'nameContains'), + + # subgroups + similar patterns + ({ + 'test-svn': '', + 'test-svn-1': '', + 'test-svn-subgroup/test-svn': '', + + }, + ['test-svn-subgroup/test-svn', + 'test-svn-subgroup/test-svn/foo/test-svn', + 'test-svn-subgroup/test-svn/trunk/example.txt', + ], + 'test-svn-subgroup/test-svn'), + ]) + def test_repo_extraction_on_subdir(self, svn_server, permissions, access_paths, expected_match): + server = svn_server.create(user_permissions=permissions) + for path in access_paths: + repo_name = server.tunnel._match_repo_name(path) + assert repo_name == expected_match + def test_run_returns_executes_command(self, svn_server): server = svn_server.create() from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper diff --git a/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py b/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py --- a/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py +++ b/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py b/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py --- a/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py +++ b/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/utils.py b/rhodecode/apps/ssh_support/utils.py --- a/rhodecode/apps/ssh_support/utils.py +++ b/rhodecode/apps/ssh_support/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/__init__.py b/rhodecode/apps/svn_support/__init__.py --- a/rhodecode/apps/svn_support/__init__.py +++ b/rhodecode/apps/svn_support/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/config_keys.py b/rhodecode/apps/svn_support/config_keys.py --- a/rhodecode/apps/svn_support/config_keys.py +++ b/rhodecode/apps/svn_support/config_keys.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/events.py b/rhodecode/apps/svn_support/events.py --- a/rhodecode/apps/svn_support/events.py +++ b/rhodecode/apps/svn_support/events.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/subscribers.py b/rhodecode/apps/svn_support/subscribers.py --- a/rhodecode/apps/svn_support/subscribers.py +++ b/rhodecode/apps/svn_support/subscribers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py b/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py --- a/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py +++ b/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/utils.py b/rhodecode/apps/svn_support/utils.py --- a/rhodecode/apps/svn_support/utils.py +++ b/rhodecode/apps/svn_support/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -37,7 +37,7 @@ log = logging.getLogger(__name__) def write_mod_dav_svn_config(settings): use_ssl = str2bool(settings['force_https']) - + file_path = settings[config_keys.config_file_path] config = _render_mod_dav_svn_config( use_ssl=use_ssl, parent_path_root=get_rhodecode_base_path(), @@ -45,7 +45,8 @@ def write_mod_dav_svn_config(settings): location_root=settings[config_keys.location_root], repo_groups=RepoGroup.get_all_repo_groups(), realm=get_rhodecode_realm(), template=settings[config_keys.template]) - _write_mod_dav_svn_config(config, settings[config_keys.config_file_path]) + _write_mod_dav_svn_config(config, file_path) + return file_path def generate_mod_dav_svn_config(registry): @@ -56,11 +57,11 @@ def generate_mod_dav_svn_config(registry repositories organized in sub folders. """ settings = registry.settings - write_mod_dav_svn_config(settings) + file_path = write_mod_dav_svn_config(settings) # Trigger an event on mod dav svn configuration change. trigger(ModDavSvnConfigChange(), registry) - + return file_path def _render_mod_dav_svn_config( parent_path_root, list_parent_path, location_root, repo_groups, realm, diff --git a/rhodecode/apps/user_group/__init__.py b/rhodecode/apps/user_group/__init__.py --- a/rhodecode/apps/user_group/__init__.py +++ b/rhodecode/apps/user_group/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/tests/__init__.py b/rhodecode/apps/user_group/tests/__init__.py --- a/rhodecode/apps/user_group/tests/__init__.py +++ b/rhodecode/apps/user_group/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/tests/test_user_groups.py b/rhodecode/apps/user_group/tests/test_user_groups.py --- a/rhodecode/apps/user_group/tests/test_user_groups.py +++ b/rhodecode/apps/user_group/tests/test_user_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/tests/test_user_groups_permissions.py b/rhodecode/apps/user_group/tests/test_user_groups_permissions.py --- a/rhodecode/apps/user_group/tests/test_user_groups_permissions.py +++ b/rhodecode/apps/user_group/tests/test_user_groups_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/views/__init__.py b/rhodecode/apps/user_group/views/__init__.py --- a/rhodecode/apps/user_group/views/__init__.py +++ b/rhodecode/apps/user_group/views/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/__init__.py b/rhodecode/apps/user_group_profile/__init__.py --- a/rhodecode/apps/user_group_profile/__init__.py +++ b/rhodecode/apps/user_group_profile/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/tests/__init__.py b/rhodecode/apps/user_group_profile/tests/__init__.py --- a/rhodecode/apps/user_group_profile/tests/__init__.py +++ b/rhodecode/apps/user_group_profile/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/tests/test_user_group.py b/rhodecode/apps/user_group_profile/tests/test_user_group.py --- a/rhodecode/apps/user_group_profile/tests/test_user_group.py +++ b/rhodecode/apps/user_group_profile/tests/test_user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/views.py b/rhodecode/apps/user_group_profile/views.py --- a/rhodecode/apps/user_group_profile/views.py +++ b/rhodecode/apps/user_group_profile/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/__init__.py b/rhodecode/apps/user_profile/__init__.py --- a/rhodecode/apps/user_profile/__init__.py +++ b/rhodecode/apps/user_profile/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/tests/__init__.py b/rhodecode/apps/user_profile/tests/__init__.py --- a/rhodecode/apps/user_profile/tests/__init__.py +++ b/rhodecode/apps/user_profile/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/tests/test_users.py b/rhodecode/apps/user_profile/tests/test_users.py --- a/rhodecode/apps/user_profile/tests/test_users.py +++ b/rhodecode/apps/user_profile/tests/test_users.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/views.py b/rhodecode/apps/user_profile/views.py --- a/rhodecode/apps/user_profile/views.py +++ b/rhodecode/apps/user_profile/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/__init__.py b/rhodecode/authentication/__init__.py --- a/rhodecode/authentication/__init__.py +++ b/rhodecode/authentication/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/base.py b/rhodecode/authentication/base.py --- a/rhodecode/authentication/base.py +++ b/rhodecode/authentication/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/interface.py b/rhodecode/authentication/interface.py --- a/rhodecode/authentication/interface.py +++ b/rhodecode/authentication/interface.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/__init__.py b/rhodecode/authentication/plugins/__init__.py --- a/rhodecode/authentication/plugins/__init__.py +++ b/rhodecode/authentication/plugins/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_crowd.py b/rhodecode/authentication/plugins/auth_crowd.py --- a/rhodecode/authentication/plugins/auth_crowd.py +++ b/rhodecode/authentication/plugins/auth_crowd.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -153,25 +153,25 @@ class CrowdServer(object): global msg msg = "" try: - rdoc = self.opener.open(request) - msg = "".join(rdoc.readlines()) + ret_doc = self.opener.open(request) + msg = ret_doc.read() if not msg and empty_response_ok: - rval = {} - rval["status"] = True - rval["error"] = "Response body was empty" + ret_val = {} + ret_val["status"] = True + ret_val["error"] = "Response body was empty" elif not noformat: - rval = json.loads(msg) - rval["status"] = True + ret_val = json.loads(msg) + ret_val["status"] = True else: - rval = "".join(rdoc.readlines()) + ret_val = msg except Exception as e: if not noformat: - rval = {"status": False, - "body": body, - "error": str(e) + "\n" + msg} + ret_val = {"status": False, + "body": body, + "error": "{}\n{}".format(e, msg)} else: - rval = None - return rval + ret_val = None + return ret_val def user_auth(self, username, password): """Authenticate a user against crowd. Returns brief information about diff --git a/rhodecode/authentication/plugins/auth_headers.py b/rhodecode/authentication/plugins/auth_headers.py --- a/rhodecode/authentication/plugins/auth_headers.py +++ b/rhodecode/authentication/plugins/auth_headers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_jasig_cas.py b/rhodecode/authentication/plugins/auth_jasig_cas.py --- a/rhodecode/authentication/plugins/auth_jasig_cas.py +++ b/rhodecode/authentication/plugins/auth_jasig_cas.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_ldap.py b/rhodecode/authentication/plugins/auth_ldap.py --- a/rhodecode/authentication/plugins/auth_ldap.py +++ b/rhodecode/authentication/plugins/auth_ldap.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -69,6 +69,12 @@ class LdapAuthnResource(AuthnPluginResou class AuthLdap(AuthLdapBase): default_tls_cert_dir = '/etc/openldap/cacerts' + scope_labels = { + ldap.SCOPE_BASE: 'SCOPE_BASE', + ldap.SCOPE_ONELEVEL: 'SCOPE_ONELEVEL', + ldap.SCOPE_SUBTREE: 'SCOPE_SUBTREE', + } + def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', tls_kind='PLAIN', tls_reqcert='DEMAND', tls_cert_file=None, tls_cert_dir=None, ldap_version=3, @@ -85,7 +91,7 @@ class AuthLdap(AuthLdapBase): self.TLS_KIND = tls_kind if self.TLS_KIND == 'LDAPS': - port = port or 689 + port = port or 636 self.ldap_server_type += 's' OPT_X_TLS_DEMAND = 2 @@ -148,19 +154,27 @@ class AuthLdap(AuthLdapBase): log.debug('simple_bind successful') return ldap_conn - def fetch_attrs_from_simple_bind(self, server, dn, username, password): + def fetch_attrs_from_simple_bind(self, ldap_conn, dn, username, password): + scope = ldap.SCOPE_BASE + scope_label = self.scope_labels.get(scope) + ldap_filter = '(objectClass=*)' + try: - log.debug('Trying simple bind with %r', dn) - server.simple_bind_s(dn, safe_str(password)) - _dn, attrs = server.search_ext_s( - dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0] + log.debug('Trying authenticated search bind with dn: %r SCOPE: %s (and filter: %s)', + dn, scope_label, ldap_filter) + ldap_conn.simple_bind_s(dn, safe_str(password)) + response = ldap_conn.search_ext_s(dn, scope, ldap_filter, attrlist=['*', '+']) - return attrs + if not response: + log.error('search bind returned empty results: %r', response) + return {} + else: + _dn, attrs = response[0] + return attrs except ldap.INVALID_CREDENTIALS: - log.debug( - "LDAP rejected password for user '%s': %s, org_exc:", - username, dn, exc_info=True) + log.debug("LDAP rejected password for user '%s': %s, org_exc:", + username, dn, exc_info=True) def authenticate_ldap(self, username, password): """ @@ -179,35 +193,38 @@ class AuthLdap(AuthLdapBase): self.validate_password(username, password) self.validate_username(username) + scope_label = self.scope_labels.get(self.SEARCH_SCOPE) ldap_conn = None try: ldap_conn = self._get_ldap_conn() filter_ = '(&%s(%s=%s))' % ( self.LDAP_FILTER, self.attr_login, username) - log.debug("Authenticating %r filter %s", self.BASE_DN, filter_) + log.debug("Authenticating %r filter %s and scope: %s", + self.BASE_DN, filter_, scope_label) - lobjects = ldap_conn.search_ext_s( - self.BASE_DN, self.SEARCH_SCOPE, filter_) + ldap_objects = ldap_conn.search_ext_s( + self.BASE_DN, self.SEARCH_SCOPE, filter_, attrlist=['*', '+']) - if not lobjects: + if not ldap_objects: log.debug("No matching LDAP objects for authentication " "of UID:'%s' username:(%s)", uid, username) raise ldap.NO_SUCH_OBJECT() - log.debug('Found matching ldap object, trying to authenticate') - for (dn, _attrs) in lobjects: + log.debug('Found %s matching ldap object[s], trying to authenticate on each one now...', len(ldap_objects)) + for (dn, _attrs) in ldap_objects: if dn is None: continue user_attrs = self.fetch_attrs_from_simple_bind( ldap_conn, dn, username, password) + if user_attrs: + log.debug('Got authenticated user attributes from DN:%s', dn) break else: raise LdapPasswordError( - 'Failed to authenticate user `{}` ' - 'with given password'.format(username)) + 'Failed to authenticate user `{}` with given password'.format(username)) except ldap.NO_SUCH_OBJECT: log.debug("LDAP says no such user '%s' (%s), org_exc:", @@ -216,8 +233,7 @@ class AuthLdap(AuthLdapBase): except ldap.SERVER_DOWN: org_exc = traceback.format_exc() raise LdapConnectionError( - "LDAP can't access authentication " - "server, org_exc:%s" % org_exc) + "LDAP can't access authentication server, org_exc:%s" % org_exc) finally: if ldap_conn: log.debug('ldap: connection release') @@ -249,7 +265,7 @@ class LdapSettingsSchema(AuthnPluginSett colander.Int(), default=389, description=_('Custom port that the LDAP server is listening on. ' - 'Default value is: 389, use 689 for LDAPS (SSL)'), + 'Default value is: 389, use 636 for LDAPS (SSL)'), preparer=strip_whitespace, title=_('Port'), validator=colander.Range(min=0, max=65536), diff --git a/rhodecode/authentication/plugins/auth_pam.py b/rhodecode/authentication/plugins/auth_pam.py --- a/rhodecode/authentication/plugins/auth_pam.py +++ b/rhodecode/authentication/plugins/auth_pam.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_rhodecode.py b/rhodecode/authentication/plugins/auth_rhodecode.py --- a/rhodecode/authentication/plugins/auth_rhodecode.py +++ b/rhodecode/authentication/plugins/auth_rhodecode.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_token.py b/rhodecode/authentication/plugins/auth_token.py --- a/rhodecode/authentication/plugins/auth_token.py +++ b/rhodecode/authentication/plugins/auth_token.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/registry.py b/rhodecode/authentication/registry.py --- a/rhodecode/authentication/registry.py +++ b/rhodecode/authentication/registry.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/routes.py b/rhodecode/authentication/routes.py --- a/rhodecode/authentication/routes.py +++ b/rhodecode/authentication/routes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/schema.py b/rhodecode/authentication/schema.py --- a/rhodecode/authentication/schema.py +++ b/rhodecode/authentication/schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/tests/conftest.py b/rhodecode/authentication/tests/conftest.py --- a/rhodecode/authentication/tests/conftest.py +++ b/rhodecode/authentication/tests/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/tests/functional/test_settings.py b/rhodecode/authentication/tests/functional/test_settings.py --- a/rhodecode/authentication/tests/functional/test_settings.py +++ b/rhodecode/authentication/tests/functional/test_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/views.py b/rhodecode/authentication/views.py --- a/rhodecode/authentication/views.py +++ b/rhodecode/authentication/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/__init__.py b/rhodecode/config/__init__.py --- a/rhodecode/config/__init__.py +++ b/rhodecode/config/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/conf.py b/rhodecode/config/conf.py --- a/rhodecode/config/conf.py +++ b/rhodecode/config/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/environment.py b/rhodecode/config/environment.py --- a/rhodecode/config/environment.py +++ b/rhodecode/config/environment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -60,10 +60,15 @@ def load_pyramid_environment(global_conf load_rcextensions(root_path=settings_merged['here']) - # Limit backends to `vcs.backends` from configuration + # Limit backends to `vcs.backends` from configuration, and preserve the order for alias in rhodecode.BACKENDS.keys(): if alias not in settings['vcs.backends']: del rhodecode.BACKENDS[alias] + + _sorted_backend = sorted(rhodecode.BACKENDS.items(), + key=lambda item: settings['vcs.backends'].index(item[0])) + rhodecode.BACKENDS = rhodecode.OrderedDict(_sorted_backend) + log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys()) # initialize vcs client and optionally run the server if enabled @@ -76,6 +81,7 @@ def load_pyramid_environment(global_conf rhodecode.PYRAMID_SETTINGS = settings_merged rhodecode.CONFIG = settings_merged + rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id() if vcs_server_enabled: connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings)) diff --git a/rhodecode/config/jsroutes.py b/rhodecode/config/jsroutes.py --- a/rhodecode/config/jsroutes.py +++ b/rhodecode/config/jsroutes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -611,6 +611,14 @@ def _sanitize_cache_settings(settings): settings, 'exception_tracker.store_path', temp_store, lower=False, default_when_empty=True) + _bool_setting( + settings, + 'exception_tracker.send_email', + 'false') + _string_setting( + settings, + 'exception_tracker.email_prefix', + '[RHODECODE ERROR]', lower=False, default_when_empty=True) # cache_perms _string_setting( diff --git a/rhodecode/config/patches.py b/rhodecode/config/patches.py --- a/rhodecode/config/patches.py +++ b/rhodecode/config/patches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/__init__.py b/rhodecode/config/rcextensions/__init__.py --- a/rhodecode/config/rcextensions/__init__.py +++ b/rhodecode/config/rcextensions/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -25,6 +25,7 @@ from .hooks import ( _create_repo_group_hook, _pre_create_user_hook, _create_user_hook, + _comment_commit_repo_hook, _delete_repo_hook, _delete_user_hook, _pre_push_hook, @@ -33,6 +34,7 @@ from .hooks import ( _pull_hook, _create_pull_request_hook, _review_pull_request_hook, + _comment_pull_request_hook, _update_pull_request_hook, _merge_pull_request_hook, _close_pull_request_hook, @@ -40,6 +42,7 @@ from .hooks import ( # set as module attributes, we use those to call hooks. *do not change this* CREATE_REPO_HOOK = _create_repo_hook +COMMENT_COMMIT_REPO_HOOK = _comment_commit_repo_hook CREATE_REPO_GROUP_HOOK = _create_repo_group_hook PRE_CREATE_USER_HOOK = _pre_create_user_hook CREATE_USER_HOOK = _create_user_hook @@ -51,6 +54,7 @@ PRE_PULL_HOOK = _pre_pull_hook PULL_HOOK = _pull_hook CREATE_PULL_REQUEST = _create_pull_request_hook REVIEW_PULL_REQUEST = _review_pull_request_hook +COMMENT_PULL_REQUEST = _comment_pull_request_hook UPDATE_PULL_REQUEST = _update_pull_request_hook MERGE_PULL_REQUEST = _merge_pull_request_hook CLOSE_PULL_REQUEST = _close_pull_request_hook diff --git a/rhodecode/config/rcextensions/examples/http_call_on_push.py b/rhodecode/config/rcextensions/examples/http_call_on_push.py --- a/rhodecode/config/rcextensions/examples/http_call_on_push.py +++ b/rhodecode/config/rcextensions/examples/http_call_on_push.py @@ -24,14 +24,13 @@ def _push_hook(*args, **kwargs): # returns list of dicts with key-val fetched from extra fields repo_extra_fields = extra_fields.run(**kwargs) - if repo_extra_fields.get('endpoint_url'): - field_metadata = repo_extra_fields['endpoint_url'] - endpoint = field_metadata['field_value'] - if endpoint: - data = { - 'project': kwargs['repository'], - } - response = http_call.run(url=endpoint, params=data) - return HookResponse(0, 'Called endpoint {}, with response {}\n'.format(endpoint, response)) + endpoint_url = extra_fields.get_field(repo_extra_fields, key='endpoint_url', default='') + + if endpoint_url: + data = { + 'project': kwargs['repository'], + } + response = http_call.run(url=endpoint_url, params=data) + return HookResponse(0, 'Called endpoint {}, with response {}\n'.format(endpoint_url, response)) return HookResponse(0, '') diff --git a/rhodecode/config/rcextensions/examples/trigger_ci_call.py b/rhodecode/config/rcextensions/examples/trigger_ci_call.py --- a/rhodecode/config/rcextensions/examples/trigger_ci_call.py +++ b/rhodecode/config/rcextensions/examples/trigger_ci_call.py @@ -24,14 +24,12 @@ def _push_hook(*args, **kwargs): # returns list of dicts with key-val fetched from extra fields repo_extra_fields = extra_fields.run(**kwargs) - if repo_extra_fields.get('endpoint_url'): - field_metadata = repo_extra_fields['endpoint_url'] - endpoint = field_metadata['field_value'] - if endpoint: - data = { - 'some_key': 'val' - } - response = http_call.run(url=endpoint, json_data=data) - return HookResponse(0, 'Called endpoint {}, with response {}'.format(endpoint, response)) + endpoint_url = extra_fields.get_field(repo_extra_fields, key='endpoint_url', default='') + if endpoint_url: + data = { + 'some_key': 'val' + } + response = http_call.run(url=endpoint_url, json_data=data) + return HookResponse(0, 'Called endpoint {}, with response {}'.format(endpoint_url, response)) return HookResponse(0, '') diff --git a/rhodecode/config/rcextensions/examples/trigger_ci_call_on_comment.py b/rhodecode/config/rcextensions/examples/trigger_ci_call_on_comment.py new file mode 100644 --- /dev/null +++ b/rhodecode/config/rcextensions/examples/trigger_ci_call_on_comment.py @@ -0,0 +1,60 @@ +# Example to trigger a CI call action on specific comment text, e.g chatops and ci +# rebuild on mention of ci bot + +@has_kwargs({ + 'repo_name': '', + 'repo_type': '', + 'description': '', + 'private': '', + 'created_on': '', + 'enable_downloads': '', + 'repo_id': '', + 'user_id': '', + 'enable_statistics': '', + 'clone_uri': '', + 'fork_id': '', + 'group_id': '', + 'created_by': '', + 'repository': '', + 'comment': '', + 'commit': '' +}) +def _comment_commit_repo_hook(*args, **kwargs): + """ + POST CREATE REPOSITORY COMMENT ON COMMIT HOOK. This function will be executed after + a comment is made on this repository commit. + + """ + from .helpers import http_call, extra_fields + from .utils import UrlTemplate + # returns list of dicts with key-val fetched from extra fields + repo_extra_fields = extra_fields.run(**kwargs) + + import rhodecode + from rc_integrations.jenkins_ci import csrf_call, get_auth, requests_retry_call + + endpoint_url = extra_fields.get_field( + repo_extra_fields, key='ci_endpoint_url', + default='http://ci.rc.com/job/rc-ce-commits/build?COMMIT_ID=${commit}') + mention_text = extra_fields.get_field( + repo_extra_fields, key='ci_mention_text', + default='@jenkins build') + + endpoint_url = UrlTemplate(endpoint_url).safe_substitute( + commit=kwargs['commit']['raw_id']) + + trigger_ci = False + comment = kwargs['comment']['comment_text'] + if mention_text in comment: + trigger_ci = True + + if trigger_ci is False: + return HookResponse(0, '') + + # call some CI based on the special coment mention marker + data = { + 'project': kwargs['repository'], + } + response = http_call.run(url=endpoint_url, params=data) + + return HookResponse(0, '') \ No newline at end of file diff --git a/rhodecode/config/rcextensions/examples/validate_commit_message_author.py b/rhodecode/config/rcextensions/examples/validate_commit_message_author.py --- a/rhodecode/config/rcextensions/examples/validate_commit_message_author.py +++ b/rhodecode/config/rcextensions/examples/validate_commit_message_author.py @@ -41,11 +41,13 @@ def _pre_push_hook(*args, **kwargs): repo_extra_fields = extra_fields.run(**kwargs) # optionally use 'extra fields' to control the logic per repo - validate_author = repo_extra_fields.get('validate_author', {}).get('field_value') + validate_author = extra_fields.get_field( + repo_extra_fields, key='validate_author', default=False) should_validate = str2bool(validate_author) # optionally store validation regex into extra fields - validation_regex = repo_extra_fields.get('validation_regex', {}).get('field_value') + validation_regex = extra_fields.get_field( + repo_extra_fields, key='validation_regex', default='') def validate_commit_message(commit_message, message_regex=None): """ diff --git a/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py b/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py --- a/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py +++ b/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py @@ -44,13 +44,16 @@ def _pre_push_hook(*args, **kwargs): # optionally use 'extra fields' to control the logic per repo # e.g store a list of patterns to be forbidden e.g `*.exe, *.dump` - forbid_files = repo_extra_fields.get('forbid_files_glob', {}).get('field_value') + forbid_files = extra_fields.get_field(repo_extra_fields, key='forbid_files_glob', + convert_type=False, default=[]) forbid_files = aslist(forbid_files) # forbid_files = ['*'] # example pattern # optionally get bytes limit for a single file, e.g 1024 for 1KB - forbid_size_over = repo_extra_fields.get('forbid_size_over', {}).get('field_value') + forbid_size_over = extra_fields.get_field(repo_extra_fields, key='forbid_size_over', + convert_type=False, default=0) + forbid_size_over = int(forbid_size_over or 0) # forbid_size_over = 1024 # example 1024 diff --git a/rhodecode/config/rcextensions/helpers/__init__.py b/rhodecode/config/rcextensions/helpers/__init__.py --- a/rhodecode/config/rcextensions/helpers/__init__.py +++ b/rhodecode/config/rcextensions/helpers/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extra_fields.py b/rhodecode/config/rcextensions/helpers/extra_fields.py --- a/rhodecode/config/rcextensions/helpers/extra_fields.py +++ b/rhodecode/config/rcextensions/helpers/extra_fields.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -53,3 +53,36 @@ def run(*args, **kwargs): fields[field.field_key] = field.get_dict() return fields + + +class _Undefined(object): + pass + + +def get_field(extra_fields_data, key, default=_Undefined(), convert_type=True): + """ + field_value = get_field(extra_fields, key='ci_endpoint_url', default='') + """ + from ..utils import str2bool, aslist + + if key not in extra_fields_data: + if isinstance(default, _Undefined): + raise ValueError('key {} not present in extra_fields'.format(key)) + return default + + # NOTE(dan): from metadata we get field_label, field_value, field_desc, field_type + field_metadata = extra_fields_data[key] + + field_value = field_metadata['field_value'] + + # NOTE(dan): empty value, use default + if not field_value and not isinstance(default, _Undefined): + return default + + if convert_type: + # 'str', 'unicode', 'list', 'tuple' + _type = field_metadata['field_type'] + if _type in ['list', 'tuple']: + field_value = aslist(field_value) + + return field_value diff --git a/rhodecode/config/rcextensions/helpers/extract_post_commits.py b/rhodecode/config/rcextensions/helpers/extract_post_commits.py --- a/rhodecode/config/rcextensions/helpers/extract_post_commits.py +++ b/rhodecode/config/rcextensions/helpers/extract_post_commits.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py --- a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py +++ b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extract_pre_files.py b/rhodecode/config/rcextensions/helpers/extract_pre_files.py --- a/rhodecode/config/rcextensions/helpers/extract_pre_files.py +++ b/rhodecode/config/rcextensions/helpers/extract_pre_files.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/http_call.py b/rhodecode/config/rcextensions/helpers/http_call.py --- a/rhodecode/config/rcextensions/helpers/http_call.py +++ b/rhodecode/config/rcextensions/helpers/http_call.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/hooks.py b/rhodecode/config/rcextensions/hooks.py --- a/rhodecode/config/rcextensions/hooks.py +++ b/rhodecode/config/rcextensions/hooks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -15,11 +15,12 @@ # This program is dual-licensed. If you wish to learn more about the # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ + import logging from .utils import DotDict, HookResponse, has_kwargs + log = logging.getLogger('rhodecode.' + __name__) - # Config shortcut to keep, all configuration in one place # Example: api_key = CONFIG.my_config.api_key CONFIG = DotDict( @@ -55,13 +56,40 @@ def _create_repo_hook(*args, **kwargs): @has_kwargs({ - 'group_name': '', - 'group_parent_id': '', + 'repo_name': '', + 'repo_type': '', + 'description': '', + 'private': '', + 'created_on': '', + 'enable_downloads': '', + 'repo_id': '', + 'user_id': '', + 'enable_statistics': '', + 'clone_uri': '', + 'fork_id': '', + 'group_id': '', + 'created_by': '', + 'repository': '', + 'comment': '', + 'commit': '' +}) +def _comment_commit_repo_hook(*args, **kwargs): + """ + POST CREATE REPOSITORY COMMENT ON COMMIT HOOK. This function will be executed after + a comment is made on this repository commit. + + """ + return HookResponse(0, '') + + +@has_kwargs({ + 'group_name': '', + 'group_parent_id': '', 'group_description': '', - 'group_id': '', - 'user_id': '', - 'created_by': '', - 'created_on': '', + 'group_id': '', + 'user_id': '', + 'created_by': '', + 'created_on': '', 'enable_locking': '' }) def _create_repo_group_hook(*args, **kwargs): @@ -73,13 +101,13 @@ def _create_repo_group_hook(*args, **kwa @has_kwargs({ - 'username': '', - 'password': '', - 'email': '', + 'username': '', + 'password': '', + 'email': '', 'firstname': '', - 'lastname': '', - 'active': '', - 'admin': '', + 'lastname': '', + 'active': '', + 'admin': '', 'created_by': '', }) def _pre_create_user_hook(*args, **kwargs): @@ -173,7 +201,7 @@ def _delete_repo_hook(*args, **kwargs): 'emails': '', 'inherit_default_permissions': '', 'deleted_by': '', - }) +}) def _delete_user_hook(*args, **kwargs): """ POST DELETE USER HOOK, this function will be executed after each @@ -348,6 +376,38 @@ def _review_pull_request_hook(*args, **k 'scm': 'type of version control "git", "hg", "svn"', 'username': 'username of actor who triggered this event', 'ip': 'ip address of actor who triggered this hook', + + 'action': '', + 'repository': 'repository name', + 'pull_request_id': '', + 'url': '', + 'title': '', + 'description': '', + 'status': '', + 'comment': '', + 'created_on': '', + 'updated_on': '', + 'commit_ids': '', + 'review_status': '', + 'mergeable': '', + 'source': '', + 'target': '', + 'author': '', + 'reviewers': '', +}) +def _comment_pull_request_hook(*args, **kwargs): + """ + This hook will be executed after comment is made on a pull request + """ + return HookResponse(0, '') + + +@has_kwargs({ + 'server_url': 'url of instance that triggered this hook', + 'config': 'path to .ini config used', + 'scm': 'type of version control "git", "hg", "svn"', + 'username': 'username of actor who triggered this event', + 'ip': 'ip address of actor who triggered this hook', 'action': '', 'repository': 'repository name', 'pull_request_id': '', diff --git a/rhodecode/config/rcextensions/utils.py b/rhodecode/config/rcextensions/utils.py --- a/rhodecode/config/rcextensions/utils.py +++ b/rhodecode/config/rcextensions/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -18,8 +18,10 @@ import logging import os +import string import functools import collections +import urllib log = logging.getLogger('rhodecode.' + __name__) @@ -186,4 +188,12 @@ def aslist(obj, sep=None, strip=True): elif obj is None: return [] else: - return [obj] \ No newline at end of file + return [obj] + + +class UrlTemplate(string.Template): + + def safe_substitute(self, **kws): + # url encode the kw for usage in url + kws = {k: urllib.quote(str(v)) for k, v in kws.items()} + return super(UrlTemplate, self).safe_substitute(**kws) diff --git a/rhodecode/config/routing_links.py b/rhodecode/config/routing_links.py --- a/rhodecode/config/routing_links.py +++ b/rhodecode/config/routing_links.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/utils.py b/rhodecode/config/utils.py --- a/rhodecode/config/utils.py +++ b/rhodecode/config/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,7 +19,6 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import os -import shlex import platform from rhodecode.model import init_model @@ -91,3 +90,12 @@ def set_instance_id(config): prefix = instance_id.lstrip('*') _platform_id = platform.uname()[1] or 'instance' config['instance_id'] = '%s%s-%s' % (prefix, _platform_id, os.getpid()) + + +def get_default_user_id(): + from rhodecode.model.db import User, Session + user_id = Session()\ + .query(User.user_id)\ + .filter(User.username == User.DEFAULT_USER)\ + .scalar() + return user_id diff --git a/rhodecode/events/__init__.py b/rhodecode/events/__init__.py --- a/rhodecode/events/__init__.py +++ b/rhodecode/events/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -53,7 +53,7 @@ from rhodecode.events.user import ( # p ) from rhodecode.events.repo import ( # pragma: no cover - RepoEvent, + RepoEvent, RepoCommitCommentEvent, RepoPreCreateEvent, RepoCreateEvent, RepoPreDeleteEvent, RepoDeleteEvent, RepoPrePushEvent, RepoPushEvent, @@ -75,4 +75,5 @@ from rhodecode.events.pullrequest import PullRequestReviewEvent, PullRequestMergeEvent, PullRequestCloseEvent, + PullRequestCommentEvent, ) diff --git a/rhodecode/events/base.py b/rhodecode/events/base.py --- a/rhodecode/events/base.py +++ b/rhodecode/events/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -44,6 +44,9 @@ class RhodecodeEvent(object): self._request = request self.utc_timestamp = datetime.datetime.utcnow() + def __repr__(self): + return '<%s:(%s)>' % (self.__class__.__name__, self.name) + def get_request(self): if self._request: return self._request @@ -116,3 +119,4 @@ class RhodeCodeIntegrationEvent(Rhodecod """ Special subclass for Integration events """ + description = '' diff --git a/rhodecode/events/interfaces.py b/rhodecode/events/interfaces.py --- a/rhodecode/events/interfaces.py +++ b/rhodecode/events/interfaces.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/pullrequest.py b/rhodecode/events/pullrequest.py --- a/rhodecode/events/pullrequest.py +++ b/rhodecode/events/pullrequest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -77,6 +77,7 @@ class PullRequestCreateEvent(PullRequest """ name = 'pullrequest-create' display_name = lazy_ugettext('pullrequest created') + description = lazy_ugettext('Event triggered after pull request was created') class PullRequestCloseEvent(PullRequestEvent): @@ -86,6 +87,7 @@ class PullRequestCloseEvent(PullRequestE """ name = 'pullrequest-close' display_name = lazy_ugettext('pullrequest closed') + description = lazy_ugettext('Event triggered after pull request was closed') class PullRequestUpdateEvent(PullRequestEvent): @@ -95,6 +97,7 @@ class PullRequestUpdateEvent(PullRequest """ name = 'pullrequest-update' display_name = lazy_ugettext('pullrequest commits updated') + description = lazy_ugettext('Event triggered after pull requests was updated') class PullRequestReviewEvent(PullRequestEvent): @@ -104,6 +107,8 @@ class PullRequestReviewEvent(PullRequest """ name = 'pullrequest-review' display_name = lazy_ugettext('pullrequest review changed') + description = lazy_ugettext('Event triggered after a review status of a ' + 'pull requests has changed to other.') def __init__(self, pullrequest, status): super(PullRequestReviewEvent, self).__init__(pullrequest) @@ -117,6 +122,8 @@ class PullRequestMergeEvent(PullRequestE """ name = 'pullrequest-merge' display_name = lazy_ugettext('pullrequest merged') + description = lazy_ugettext('Event triggered after a successful merge operation ' + 'was executed on a pull request') class PullRequestCommentEvent(PullRequestEvent): @@ -126,6 +133,8 @@ class PullRequestCommentEvent(PullReques """ name = 'pullrequest-comment' display_name = lazy_ugettext('pullrequest commented') + description = lazy_ugettext('Event triggered after a comment was made on a code ' + 'in the pull request') def __init__(self, pullrequest, comment): super(PullRequestCommentEvent, self).__init__(pullrequest) diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py --- a/rhodecode/events/repo.py +++ b/rhodecode/events/repo.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -181,6 +181,40 @@ class RepoEvent(RhodeCodeIntegrationEven return data +class RepoCommitCommentEvent(RepoEvent): + """ + An instance of this class is emitted as an :term:`event` after a comment is made + on repository commit. + """ + + name = 'repo-commit-comment' + display_name = lazy_ugettext('repository commit comment') + description = lazy_ugettext('Event triggered after a comment was made ' + 'on commit inside a repository') + + def __init__(self, repo, commit, comment): + super(RepoCommitCommentEvent, self).__init__(repo) + self.commit = commit + self.comment = comment + + def as_dict(self): + data = super(RepoCommitCommentEvent, self).as_dict() + data['commit'] = { + 'commit_id': self.commit.raw_id, + 'commit_message': self.commit.message, + 'commit_branch': self.commit.branch, + } + + data['comment'] = { + 'comment_id': self.comment.comment_id, + 'comment_text': self.comment.text, + 'comment_type': self.comment.comment_type, + 'comment_f_path': self.comment.f_path, + 'comment_line_no': self.comment.line_no, + } + return data + + class RepoPreCreateEvent(RepoEvent): """ An instance of this class is emitted as an :term:`event` before a repo is @@ -188,6 +222,7 @@ class RepoPreCreateEvent(RepoEvent): """ name = 'repo-pre-create' display_name = lazy_ugettext('repository pre create') + description = lazy_ugettext('Event triggered before repository is created') class RepoCreateEvent(RepoEvent): @@ -197,6 +232,7 @@ class RepoCreateEvent(RepoEvent): """ name = 'repo-create' display_name = lazy_ugettext('repository created') + description = lazy_ugettext('Event triggered after repository was created') class RepoPreDeleteEvent(RepoEvent): @@ -206,6 +242,7 @@ class RepoPreDeleteEvent(RepoEvent): """ name = 'repo-pre-delete' display_name = lazy_ugettext('repository pre delete') + description = lazy_ugettext('Event triggered before a repository is deleted') class RepoDeleteEvent(RepoEvent): @@ -215,6 +252,7 @@ class RepoDeleteEvent(RepoEvent): """ name = 'repo-delete' display_name = lazy_ugettext('repository deleted') + description = lazy_ugettext('Event triggered after repository was deleted') class RepoVCSEvent(RepoEvent): @@ -255,6 +293,7 @@ class RepoPrePullEvent(RepoVCSEvent): """ name = 'repo-pre-pull' display_name = lazy_ugettext('repository pre pull') + description = lazy_ugettext('Event triggered before repository code is pulled') class RepoPullEvent(RepoVCSEvent): @@ -264,6 +303,7 @@ class RepoPullEvent(RepoVCSEvent): """ name = 'repo-pull' display_name = lazy_ugettext('repository pull') + description = lazy_ugettext('Event triggered after repository code was pulled') class RepoPrePushEvent(RepoVCSEvent): @@ -273,6 +313,8 @@ class RepoPrePushEvent(RepoVCSEvent): """ name = 'repo-pre-push' display_name = lazy_ugettext('repository pre push') + description = lazy_ugettext('Event triggered before the code is ' + 'pushed to a repository') class RepoPushEvent(RepoVCSEvent): @@ -284,6 +326,8 @@ class RepoPushEvent(RepoVCSEvent): """ name = 'repo-push' display_name = lazy_ugettext('repository push') + description = lazy_ugettext('Event triggered after the code was ' + 'pushed to a repository') def __init__(self, repo_name, pushed_commit_ids, extras): super(RepoPushEvent, self).__init__(repo_name, extras) diff --git a/rhodecode/events/repo_group.py b/rhodecode/events/repo_group.py --- a/rhodecode/events/repo_group.py +++ b/rhodecode/events/repo_group.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -60,6 +60,7 @@ class RepoGroupCreateEvent(RepoGroupEven """ name = 'repo-group-create' display_name = lazy_ugettext('repository group created') + description = lazy_ugettext('Event triggered after a repository group was created') class RepoGroupDeleteEvent(RepoGroupEvent): @@ -69,6 +70,7 @@ class RepoGroupDeleteEvent(RepoGroupEven """ name = 'repo-group-delete' display_name = lazy_ugettext('repository group deleted') + description = lazy_ugettext('Event triggered after a repository group was deleted') class RepoGroupUpdateEvent(RepoGroupEvent): @@ -78,3 +80,4 @@ class RepoGroupUpdateEvent(RepoGroupEven """ name = 'repo-group-update' display_name = lazy_ugettext('repository group update') + description = lazy_ugettext('Event triggered after a repository group was updated') diff --git a/rhodecode/events/user.py b/rhodecode/events/user.py --- a/rhodecode/events/user.py +++ b/rhodecode/events/user.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/forms/__init__.py b/rhodecode/forms/__init__.py --- a/rhodecode/forms/__init__.py +++ b/rhodecode/forms/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/__init__.py b/rhodecode/integrations/__init__.py --- a/rhodecode/integrations/__init__.py +++ b/rhodecode/integrations/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/registry.py b/rhodecode/integrations/registry.py --- a/rhodecode/integrations/registry.py +++ b/rhodecode/integrations/registry.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/routes.py b/rhodecode/integrations/routes.py --- a/rhodecode/integrations/routes.py +++ b/rhodecode/integrations/routes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/schema.py b/rhodecode/integrations/schema.py --- a/rhodecode/integrations/schema.py +++ b/rhodecode/integrations/schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/tests/__init__.py b/rhodecode/integrations/tests/__init__.py --- a/rhodecode/integrations/tests/__init__.py +++ b/rhodecode/integrations/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/tests/test_integrations.py b/rhodecode/integrations/tests/test_integrations.py --- a/rhodecode/integrations/tests/test_integrations.py +++ b/rhodecode/integrations/tests/test_integrations.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/__init__.py b/rhodecode/integrations/types/__init__.py --- a/rhodecode/integrations/types/__init__.py +++ b/rhodecode/integrations/types/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/base.py b/rhodecode/integrations/types/base.py --- a/rhodecode/integrations/types/base.py +++ b/rhodecode/integrations/types/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -125,6 +125,19 @@ class IntegrationTypeBase(object): """ return colander.Schema() + def event_enabled(self, event): + """ + Checks if submitted event is enabled based on the plugin settings + :param event: + :return: bool + """ + allowed_events = self.settings['events'] + if event.name not in allowed_events: + log.debug('event ignored: %r event %s not in allowed set of events %s', + event, event.name, allowed_events) + return False + return True + class EEIntegration(IntegrationTypeBase): description = 'Integration available in RhodeCode EE edition.' @@ -139,30 +152,57 @@ class EEIntegration(IntegrationTypeBase) # Helpers # # updating this required to update the `common_vars` as well. WEBHOOK_URL_VARS = [ - ('event_name', 'Unique name of the event type, e.g pullrequest-update'), - ('repo_name', 'Full name of the repository'), - ('repo_type', 'VCS type of repository'), - ('repo_id', 'Unique id of repository'), - ('repo_url', 'Repository url'), + # GENERAL + ('General', [ + ('event_name', 'Unique name of the event type, e.g pullrequest-update'), + ('repo_name', 'Full name of the repository'), + ('repo_type', 'VCS type of repository'), + ('repo_id', 'Unique id of repository'), + ('repo_url', 'Repository url'), + ] + ), # extra repo fields - ('extra:', 'Extra repo variables, read from its settings.'), - + ('Repository', [ + ('extra:', 'Extra repo variables, read from its settings.'), + ] + ), # special attrs below that we handle, using multi-call - ('branch', 'Name of each branch submitted, if any.'), - ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'), - ('commit_id', 'ID (full sha) of each commit submitted, if any.'), - + ('Commit push - Multicalls', [ + ('branch', 'Name of each branch submitted, if any.'), + ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'), + ('commit_id', 'ID (full sha) of each commit submitted, if any.'), + ] + ), # pr events vars - ('pull_request_id', 'Unique ID of the pull request.'), - ('pull_request_title', 'Title of the pull request.'), - ('pull_request_url', 'Pull request url.'), - ('pull_request_shadow_url', 'Pull request shadow repo clone url.'), - ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. ' - 'Changes after PR update'), + ('Pull request', [ + ('pull_request_id', 'Unique ID of the pull request.'), + ('pull_request_title', 'Title of the pull request.'), + ('pull_request_url', 'Pull request url.'), + ('pull_request_shadow_url', 'Pull request shadow repo clone url.'), + ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. ' + 'Changes after PR update'), + ] + ), + # commit comment event vars + ('Commit comment', [ + ('commit_comment_id', 'Unique ID of the comment made on a commit.'), + ('commit_comment_text', 'Text of commit comment.'), + ('commit_comment_type', 'Type of comment, e.g note/todo.'), + ('commit_comment_f_path', 'Optionally path of file for inline comments.'), + ('commit_comment_line_no', 'Line number of the file: eg o10, or n200'), + + ('commit_comment_commit_id', 'Commit id that comment was left at.'), + ('commit_comment_commit_branch', 'Commit branch that comment was left at'), + ('commit_comment_commit_message', 'Commit message that comment was left at'), + ] + ), # user who triggers the call - ('username', 'User who triggered the call.'), - ('user_id', 'User id who triggered the call.'), + ('Caller', [ + ('username', 'User who triggered the call.'), + ('user_id', 'User id who triggered the call.'), + ] + ), ] # common vars for url template used for CI plugins. Shared with webhook @@ -271,6 +311,26 @@ class WebhookDataHandler(CommitParsingDa return url_calls + def repo_commit_comment_handler(self, event, data): + url = self.get_base_parsed_template(data) + log.debug('register %s call(%s) to url %s', self.name, event, url) + comment_vars = [ + ('commit_comment_id', data['comment']['comment_id']), + ('commit_comment_text', data['comment']['comment_text']), + ('commit_comment_type', data['comment']['comment_type']), + + ('commit_comment_f_path', data['comment']['comment_f_path']), + ('commit_comment_line_no', data['comment']['comment_line_no']), + + ('commit_comment_commit_id', data['commit']['commit_id']), + ('commit_comment_commit_branch', data['commit']['commit_branch']), + ('commit_comment_commit_message', data['commit']['commit_message']), + ] + for k, v in comment_vars: + url = UrlTmpl(url).safe_substitute(**{k: v}) + + return [(url, self.headers, data)] + def repo_create_event_handler(self, event, data): url = self.get_base_parsed_template(data) log.debug('register %s call(%s) to url %s', self.name, event, url) @@ -298,12 +358,13 @@ class WebhookDataHandler(CommitParsingDa return self.repo_push_event_handler(event, data) elif isinstance(event, events.RepoCreateEvent): return self.repo_create_event_handler(event, data) + elif isinstance(event, events.RepoCommitCommentEvent): + return self.repo_commit_comment_handler(event, data) elif isinstance(event, events.PullRequestEvent): return self.pull_request_event_handler(event, data) else: raise ValueError( - 'event type `%s` not in supported list: %s' % ( - event.__class__, events)) + 'event type `{}` has no handler defined'.format(event.__class__)) def get_auth(settings): @@ -320,9 +381,13 @@ def get_web_token(settings): def get_url_vars(url_vars): - return '\n'.join( - '{} - {}'.format('${' + key + '}', explanation) - for key, explanation in url_vars) + items = [] + + for section, section_items in url_vars: + items.append('\n*{}*'.format(section)) + for key, explanation in section_items: + items.append(' {} - {}'.format('${' + key + '}', explanation)) + return '\n'.join(items) def render_with_traceback(template, *args, **kwargs): diff --git a/rhodecode/integrations/types/email.py b/rhodecode/integrations/types/email.py --- a/rhodecode/integrations/types/email.py +++ b/rhodecode/integrations/types/email.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,13 +19,14 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ from __future__ import unicode_literals -import deform import logging + import colander - +import deform.widget from mako.template import Template from rhodecode import events +from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc from rhodecode.translation import _ from rhodecode.lib.celerylib import run_task from rhodecode.lib.celerylib import tasks @@ -174,6 +175,10 @@ class EmailIntegrationType(IntegrationTy display_name = _('Email') description = _('Send repo push summaries to a list of recipients via email') + valid_events = [ + events.RepoPushEvent + ] + @classmethod def icon(cls): return ''' @@ -240,59 +245,86 @@ class EmailIntegrationType(IntegrationTy def settings_schema(self): schema = EmailSettingsSchema() + schema.add(colander.SchemaNode( + colander.Set(), + widget=CheckboxChoiceWidgetDesc( + values=sorted( + [(e.name, e.display_name, e.description) for e in self.valid_events] + ), + ), + description="List of events activated for this integration", + name='events' + )) return schema def send_event(self, event): - data = event.as_dict() - log.debug('got event: %r', event) + log.debug('handling event %s with integration %s', event.name, self) + + if event.__class__ not in self.valid_events: + log.debug('event %r not present in valid event list (%s)', event, self.valid_events) + return + + if not self.event_enabled(event): + # NOTE(marcink): for legacy reasons we're skipping this check... + # since the email event haven't had any settings... + pass + handler = EmailEventHandler(self.settings) + handler(event, event_data=event.as_dict()) + + +class EmailEventHandler(object): + def __init__(self, integration_settings): + self.integration_settings = integration_settings + + def __call__(self, event, event_data): if isinstance(event, events.RepoPushEvent): - repo_push_handler(data, self.settings) + self.repo_push_handler(event, event_data) else: log.debug('ignoring event: %r', event) - -def repo_push_handler(data, settings): - commit_num = len(data['push']['commits']) - server_url = data['server_url'] + def repo_push_handler(self, event, data): + commit_num = len(data['push']['commits']) + server_url = data['server_url'] - if commit_num == 1: - if data['push']['branches']: - _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}' - else: - _subject = '[{repo_name}] {author} pushed {commit_num} commit' - subject = _subject.format( - author=data['actor']['username'], - repo_name=data['repo']['repo_name'], - commit_num=commit_num, - branches=', '.join( - branch['name'] for branch in data['push']['branches']) - ) - else: - if data['push']['branches']: - _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}' + if commit_num == 1: + if data['push']['branches']: + _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}' + else: + _subject = '[{repo_name}] {author} pushed {commit_num} commit' + subject = _subject.format( + author=data['actor']['username'], + repo_name=data['repo']['repo_name'], + commit_num=commit_num, + branches=', '.join( + branch['name'] for branch in data['push']['branches']) + ) else: - _subject = '[{repo_name}] {author} pushed {commit_num} commits' - subject = _subject.format( - author=data['actor']['username'], - repo_name=data['repo']['repo_name'], - commit_num=commit_num, - branches=', '.join( - branch['name'] for branch in data['push']['branches'])) + if data['push']['branches']: + _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}' + else: + _subject = '[{repo_name}] {author} pushed {commit_num} commits' + subject = _subject.format( + author=data['actor']['username'], + repo_name=data['repo']['repo_name'], + commit_num=commit_num, + branches=', '.join( + branch['name'] for branch in data['push']['branches'])) - email_body_plaintext = render_with_traceback( - REPO_PUSH_TEMPLATE_PLAINTEXT, - data=data, - subject=subject, - instance_url=server_url) + email_body_plaintext = render_with_traceback( + REPO_PUSH_TEMPLATE_PLAINTEXT, + data=data, + subject=subject, + instance_url=server_url) - email_body_html = render_with_traceback( - REPO_PUSH_TEMPLATE_HTML, - data=data, - subject=subject, - instance_url=server_url) + email_body_html = render_with_traceback( + REPO_PUSH_TEMPLATE_HTML, + data=data, + subject=subject, + instance_url=server_url) - for email_address in settings['recipients']: - run_task( - tasks.send_email, email_address, subject, - email_body_plaintext, email_body_html) + recipients = self.integration_settings['recipients'] + for email_address in recipients: + run_task( + tasks.send_email, email_address, subject, + email_body_plaintext, email_body_html) diff --git a/rhodecode/integrations/types/hipchat.py b/rhodecode/integrations/types/hipchat.py --- a/rhodecode/integrations/types/hipchat.py +++ b/rhodecode/integrations/types/hipchat.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -26,6 +26,7 @@ import colander import textwrap from mako.template import Template from rhodecode import events +from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc from rhodecode.translation import _ from rhodecode.lib import helpers as h from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask @@ -119,13 +120,10 @@ class HipchatIntegrationType(Integration def send_event(self, event): if event.__class__ not in self.valid_events: - log.debug('event not valid: %r', event) + log.debug('event %r not present in valid event list (%s)', event, self.valid_events) return - allowed_events = self.settings['events'] - if event.name not in allowed_events: - log.debug('event ignored: %r event %s not in allowed events %s', - event, event.name, allowed_events) + if not self.event_enabled(event): return data = event.as_dict() @@ -133,8 +131,6 @@ class HipchatIntegrationType(Integration text = '%s caused a %s event' % ( data['actor']['username'], event.name) - log.debug('handling hipchat event for %s', event.name) - if isinstance(event, events.PullRequestCommentEvent): text = self.format_pull_request_comment_event(event, data) elif isinstance(event, events.PullRequestReviewEvent): @@ -154,12 +150,12 @@ class HipchatIntegrationType(Integration schema = HipchatSettingsSchema() schema.add(colander.SchemaNode( colander.Set(), - widget=deform.widget.CheckboxChoiceWidget( + widget=CheckboxChoiceWidgetDesc( values=sorted( - [(e.name, e.display_name) for e in self.valid_events] - ) + [(e.name, e.display_name, e.description) for e in self.valid_events] + ), ), - description="Events activated for this integration", + description="List of events activated for this integration", name='events' )) diff --git a/rhodecode/integrations/types/slack.py b/rhodecode/integrations/types/slack.py --- a/rhodecode/integrations/types/slack.py +++ b/rhodecode/integrations/types/slack.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -30,6 +30,7 @@ import colander from mako.template import Template from rhodecode import events +from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc from rhodecode.translation import _ from rhodecode.lib import helpers as h from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask @@ -134,14 +135,13 @@ class SlackIntegrationType(IntegrationTy ] def send_event(self, event): + log.debug('handling event %s with integration %s', event.name, self) + if event.__class__ not in self.valid_events: - log.debug('event not valid: %r', event) + log.debug('event %r not present in valid event list (%s)', event, self.valid_events) return - allowed_events = self.settings['events'] - if event.name not in allowed_events: - log.debug('event ignored: %r event %s not in allowed events %s', - event, event.name, allowed_events) + if not self.event_enabled(event): return data = event.as_dict() @@ -154,8 +154,6 @@ class SlackIntegrationType(IntegrationTy fields = None overrides = None - log.debug('handling slack event for %s', event.name) - if isinstance(event, events.PullRequestCommentEvent): (title, text, fields, overrides) \ = self.format_pull_request_comment_event(event, data) @@ -176,12 +174,12 @@ class SlackIntegrationType(IntegrationTy schema = SlackSettingsSchema() schema.add(colander.SchemaNode( colander.Set(), - widget=deform.widget.CheckboxChoiceWidget( + widget=CheckboxChoiceWidgetDesc( values=sorted( - [(e.name, e.display_name) for e in self.valid_events] - ) + [(e.name, e.display_name, e.description) for e in self.valid_events] + ), ), - description="Events activated for this integration", + description="List of events activated for this integration", name='events' )) diff --git a/rhodecode/integrations/types/webhook.py b/rhodecode/integrations/types/webhook.py --- a/rhodecode/integrations/types/webhook.py +++ b/rhodecode/integrations/types/webhook.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -20,13 +20,14 @@ from __future__ import unicode_literals -import deform import deform.widget import logging import colander import rhodecode from rhodecode import events +from rhodecode.lib.colander_utils import strip_whitespace +from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc from rhodecode.translation import _ from rhodecode.integrations.types.base import ( IntegrationTypeBase, get_auth, get_web_token, get_url_vars, @@ -53,11 +54,12 @@ class WebhookSettingsSchema(colander.Sch 'objects in data in such cases.'), missing=colander.required, required=True, + preparer=strip_whitespace, validator=colander.url, widget=widgets.CodeMirrorWidget( help_block_collapsable_name='Show url variables', help_block_collapsable=( - 'E.g http://my-serv/trigger_job/${{event_name}}' + 'E.g http://my-serv.com/trigger_job/${{event_name}}' '?PR_ID=${{pull_request_id}}' '\nFull list of vars:\n{}'.format(URL_VARS)), codemirror_mode='text', @@ -146,34 +148,31 @@ class WebhookIntegrationType(Integration events.PullRequestCreateEvent, events.RepoPushEvent, events.RepoCreateEvent, + events.RepoCommitCommentEvent, ] def settings_schema(self): schema = WebhookSettingsSchema() schema.add(colander.SchemaNode( colander.Set(), - widget=deform.widget.CheckboxChoiceWidget( + widget=CheckboxChoiceWidgetDesc( values=sorted( - [(e.name, e.display_name) for e in self.valid_events] - ) + [(e.name, e.display_name, e.description) for e in self.valid_events] + ), ), - description="Events activated for this integration", + description="List of events activated for this integration", name='events' )) return schema def send_event(self, event): - log.debug( - 'handling event %s with Webhook integration %s', event.name, self) + log.debug('handling event %s with integration %s', event.name, self) if event.__class__ not in self.valid_events: - log.debug('event not valid: %r', event) + log.debug('event %r not present in valid event list (%s)', event, self.valid_events) return - allowed_events = self.settings['events'] - if event.name not in allowed_events: - log.debug('event ignored: %r event %s not in allowed events %s', - event, event.name, allowed_events) + if not self.event_enabled(event): return data = event.as_dict() diff --git a/rhodecode/integrations/views.py b/rhodecode/integrations/views.py --- a/rhodecode/integrations/views.py +++ b/rhodecode/integrations/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/__init__.py b/rhodecode/lib/__init__.py --- a/rhodecode/lib/__init__.py +++ b/rhodecode/lib/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/_vendor/__init__.py b/rhodecode/lib/_vendor/__init__.py --- a/rhodecode/lib/_vendor/__init__.py +++ b/rhodecode/lib/_vendor/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/action_parser.py b/rhodecode/lib/action_parser.py --- a/rhodecode/lib/action_parser.py +++ b/rhodecode/lib/action_parser.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/audit_logger.py b/rhodecode/lib/audit_logger.py --- a/rhodecode/lib/audit_logger.py +++ b/rhodecode/lib/audit_logger.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py --- a/rhodecode/lib/auth.py +++ b/rhodecode/lib/auth.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -23,6 +23,8 @@ authentication and permission libraries """ import os + +import colander import time import collections import fnmatch @@ -45,15 +47,14 @@ from rhodecode.model import meta from rhodecode.model.meta import Session from rhodecode.model.user import UserModel from rhodecode.model.db import ( - User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember, - UserIpMap, UserApiKeys, RepoGroup, UserGroup) + false, User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember, + UserIpMap, UserApiKeys, RepoGroup, UserGroup, UserNotice) from rhodecode.lib import rc_cache from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1 from rhodecode.lib.utils import ( get_repo_slug, get_repo_group_slug, get_user_group_slug) from rhodecode.lib.caching_query import FromCache - if rhodecode.is_unix: import bcrypt @@ -1312,7 +1313,10 @@ class AuthUser(object): if not perms: perms = AuthUser.repo_read_perms - def _cached_repo_acl(user_id, perm_def, _name_filter): + if not isinstance(perms, list): + raise ValueError('perms parameter must be a list got {} instead'.format(perms)) + + def _cached_repo_acl(perm_def, _name_filter): qry = Repository.query() if _name_filter: ilike_expression = u'%{}%'.format(safe_unicode(_name_filter)) @@ -1322,7 +1326,21 @@ class AuthUser(object): return [x.repo_id for x in RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})] - return _cached_repo_acl(self.user_id, perms, name_filter) + log.debug('Computing REPO ACL IDS user %s', self) + + cache_namespace_uid = 'cache_user_repo_acl_ids.{}'.format(self.user_id) + region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid) + + @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache) + def compute_repo_acl_ids(cache_ver, user_id, perm_def, _name_filter): + return _cached_repo_acl(perm_def, _name_filter) + + start = time.time() + result = compute_repo_acl_ids('v1', self.user_id, perms, name_filter) + total = time.time() - start + log.debug('REPO ACL IDS for user %s computed in %.4fs', self, total) + + return result def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False): if not perms: @@ -1346,7 +1364,10 @@ class AuthUser(object): if not perms: perms = AuthUser.repo_group_read_perms - def _cached_repo_group_acl(user_id, perm_def, _name_filter): + if not isinstance(perms, list): + raise ValueError('perms parameter must be a list got {} instead'.format(perms)) + + def _cached_repo_group_acl(perm_def, _name_filter): qry = RepoGroup.query() if _name_filter: ilike_expression = u'%{}%'.format(safe_unicode(_name_filter)) @@ -1356,7 +1377,21 @@ class AuthUser(object): return [x.group_id for x in RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})] - return _cached_repo_group_acl(self.user_id, perms, name_filter) + log.debug('Computing REPO GROUP ACL IDS user %s', self) + + cache_namespace_uid = 'cache_user_repo_group_acl_ids.{}'.format(self.user_id) + region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid) + + @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache) + def compute_repo_group_acl_ids(cache_ver, user_id, perm_def, _name_filter): + return _cached_repo_group_acl(perm_def, _name_filter) + + start = time.time() + result = compute_repo_group_acl_ids('v1', self.user_id, perms, name_filter) + total = time.time() - start + log.debug('REPO GROUP ACL IDS for user %s computed in %.4fs', self, total) + + return result def user_group_acl_ids_from_stack(self, perms=None, cache=False): if not perms: @@ -1378,17 +1413,34 @@ class AuthUser(object): if not perms: perms = AuthUser.user_group_read_perms - def _cached_user_group_acl(user_id, perm_def, name_filter): + if not isinstance(perms, list): + raise ValueError('perms parameter must be a list got {} instead'.format(perms)) + + def _cached_user_group_acl(perm_def, _name_filter): qry = UserGroup.query() - if name_filter: - ilike_expression = u'%{}%'.format(safe_unicode(name_filter)) + if _name_filter: + ilike_expression = u'%{}%'.format(safe_unicode(_name_filter)) qry = qry.filter( UserGroup.users_group_name.ilike(ilike_expression)) return [x.users_group_id for x in UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})] - return _cached_user_group_acl(self.user_id, perms, name_filter) + log.debug('Computing USER GROUP ACL IDS user %s', self) + + cache_namespace_uid = 'cache_user_user_group_acl_ids.{}'.format(self.user_id) + region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid) + + @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache) + def compute_user_group_acl_ids(cache_ver, user_id, perm_def, _name_filter): + return _cached_user_group_acl(perm_def, _name_filter) + + start = time.time() + result = compute_user_group_acl_ids('v1', self.user_id, perms, name_filter) + total = time.time() - start + log.debug('USER GROUP ACL IDS for user %s computed in %.4fs', self, total) + + return result @property def ip_allowed(self): @@ -1402,6 +1454,7 @@ class AuthUser(object): inherit = self.inherit_default_permissions return AuthUser.check_ip_allowed(self.user_id, self.ip_addr, inherit_from_default=inherit) + @property def personal_repo_group(self): return RepoGroup.get_user_personal_repo_group(self.user_id) @@ -1455,9 +1508,40 @@ class AuthUser(object): return rule, default_perm + def get_notice_messages(self): + + notice_level = 'notice-error' + notice_messages = [] + if self.is_default: + return [], notice_level + + notices = UserNotice.query()\ + .filter(UserNotice.user_id == self.user_id)\ + .filter(UserNotice.notice_read == false())\ + .all() + + try: + for entry in notices: + + msg = { + 'msg_id': entry.user_notice_id, + 'level': entry.notification_level, + 'subject': entry.notice_subject, + 'body': entry.notice_body, + } + notice_messages.append(msg) + + log.debug('Got user %s %s messages', self, len(notice_messages)) + + levels = [x['level'] for x in notice_messages] + notice_level = 'notice-error' if 'error' in levels else 'notice-warning' + except Exception: + pass + + return notice_messages, notice_level + def __repr__(self): - return ""\ - % (self.user_id, self.username, self.ip_addr, self.is_authenticated) + return self.repr_user(self.user_id, self.username, self.ip_addr, self.is_authenticated) def set_authenticated(self, authenticated=True): if self.user_id != self.anonymous_user.user_id: @@ -1472,6 +1556,11 @@ class AuthUser(object): } @classmethod + def repr_user(cls, user_id=0, username='ANONYMOUS', ip='0.0.0.0', is_authenticated=False): + tmpl = "" + return tmpl.format(user_id, username, ip, is_authenticated) + + @classmethod def from_cookie_store(cls, cookie_store): """ Creates AuthUser from a cookie store diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -283,7 +283,7 @@ def get_current_lang(request): return getattr(request, '_LOCALE_', request.locale_name) -def attach_context_attributes(context, request, user_id=None): +def attach_context_attributes(context, request, user_id=None, is_api=None): """ Attach variables into template context called `c`. """ @@ -379,7 +379,8 @@ def attach_context_attributes(context, r "sideside": "sideside" }.get(request.GET.get('diffmode')) - is_api = hasattr(request, 'rpc_user') + if is_api is not None: + is_api = hasattr(request, 'rpc_user') session_attrs = { # defaults "clone_url_format": "http", @@ -436,7 +437,7 @@ def attach_context_attributes(context, r context.csrf_token = csrf_token context.backends = rhodecode.BACKENDS.keys() - context.backends.sort() + unread_count = 0 user_bookmark_list = [] if user_id: diff --git a/rhodecode/lib/caching_query.py b/rhodecode/lib/caching_query.py --- a/rhodecode/lib/caching_query.py +++ b/rhodecode/lib/caching_query.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/__init__.py b/rhodecode/lib/celerylib/__init__.py --- a/rhodecode/lib/celerylib/__init__.py +++ b/rhodecode/lib/celerylib/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/loader.py b/rhodecode/lib/celerylib/loader.py --- a/rhodecode/lib/celerylib/loader.py +++ b/rhodecode/lib/celerylib/loader.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/scheduler.py b/rhodecode/lib/celerylib/scheduler.py --- a/rhodecode/lib/celerylib/scheduler.py +++ b/rhodecode/lib/celerylib/scheduler.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py --- a/rhodecode/lib/celerylib/tasks.py +++ b/rhodecode/lib/celerylib/tasks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -49,6 +49,7 @@ def send_email(recipients, subject, body :param subject: subject of the mail :param body: body of the mail :param html_body: html version of body + :param email_config: specify custom configuration for mailer """ log = get_logger(send_email) diff --git a/rhodecode/lib/celerylib/utils.py b/rhodecode/lib/celerylib/utils.py --- a/rhodecode/lib/celerylib/utils.py +++ b/rhodecode/lib/celerylib/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/channelstream.py b/rhodecode/lib/channelstream.py --- a/rhodecode/lib/channelstream.py +++ b/rhodecode/lib/channelstream.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/codeblocks.py b/rhodecode/lib/codeblocks.py --- a/rhodecode/lib/codeblocks.py +++ b/rhodecode/lib/codeblocks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/colander_utils.py b/rhodecode/lib/colander_utils.py --- a/rhodecode/lib/colander_utils.py +++ b/rhodecode/lib/colander_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/colored_formatter.py b/rhodecode/lib/colored_formatter.py --- a/rhodecode/lib/colored_formatter.py +++ b/rhodecode/lib/colored_formatter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/compat.py b/rhodecode/lib/compat.py --- a/rhodecode/lib/compat.py +++ b/rhodecode/lib/compat.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/datelib.py b/rhodecode/lib/datelib.py --- a/rhodecode/lib/datelib.py +++ b/rhodecode/lib/datelib.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/__init__.py b/rhodecode/lib/dbmigrate/__init__.py --- a/rhodecode/lib/dbmigrate/__init__.py +++ b/rhodecode/lib/dbmigrate/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/__init__.py b/rhodecode/lib/dbmigrate/migrate/changeset/__init__.py --- a/rhodecode/lib/dbmigrate/migrate/changeset/__init__.py +++ b/rhodecode/lib/dbmigrate/migrate/changeset/__init__.py @@ -12,10 +12,11 @@ from sqlalchemy import __version__ as _s warnings.simplefilter('always', DeprecationWarning) -_sa_version = tuple(int(re.match("\d+", x).group(0)) - for x in _sa_version.split(".")) +_sa_version = tuple(int(re.match("\d+", x).group(0)) for x in _sa_version.split(".")) SQLA_07 = _sa_version >= (0, 7) SQLA_08 = _sa_version >= (0, 8) +SQLA_09 = _sa_version >= (0, 9) +SQLA_10 = _sa_version >= (1, 0) del re del _sa_version @@ -23,8 +24,8 @@ del _sa_version from rhodecode.lib.dbmigrate.migrate.changeset.schema import * from rhodecode.lib.dbmigrate.migrate.changeset.constraint import * -sqlalchemy.schema.Table.__bases__ += (ChangesetTable,) -sqlalchemy.schema.Column.__bases__ += (ChangesetColumn,) -sqlalchemy.schema.Index.__bases__ += (ChangesetIndex,) +sqlalchemy.schema.Table.__bases__ += (ChangesetTable, ) +sqlalchemy.schema.Column.__bases__ += (ChangesetColumn, ) +sqlalchemy.schema.Index.__bases__ += (ChangesetIndex, ) -sqlalchemy.schema.DefaultClause.__bases__ += (ChangesetDefaultClause,) +sqlalchemy.schema.DefaultClause.__bases__ += (ChangesetDefaultClause, ) diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py b/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py --- a/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py +++ b/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py @@ -97,7 +97,6 @@ class ANSIColumnGenerator(AlterTableVisi table = self.start_alter_table(column) self.append("ADD ") - self.append(self.get_column_specification(column)) for cons in column.constraints: diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/constraint.py b/rhodecode/lib/dbmigrate/migrate/changeset/constraint.py --- a/rhodecode/lib/dbmigrate/migrate/changeset/constraint.py +++ b/rhodecode/lib/dbmigrate/migrate/changeset/constraint.py @@ -111,7 +111,7 @@ class ForeignKeyConstraint(ConstraintCha refcolnames, reftable = self._normalize_columns(refcolumns, table_name=True) super(ForeignKeyConstraint, self).__init__( - colnames, refcolnames, *args,**kwargs + colnames, refcolnames, *args, **kwargs ) if table is not None: self._set_parent(table) diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py b/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py --- a/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py +++ b/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py @@ -3,16 +3,20 @@ .. _`SQLite`: http://www.sqlite.org/ """ -from UserDict import DictMixin +try: # Python 3 + from collections.abc import MutableMapping as DictMixin +except ImportError: # Python 2 + from UserDict import DictMixin from copy import copy import re from sqlalchemy.databases import sqlite as sa_base +from sqlalchemy.schema import ForeignKeyConstraint from sqlalchemy.schema import UniqueConstraint from rhodecode.lib.dbmigrate.migrate import exceptions from rhodecode.lib.dbmigrate.migrate.changeset import ansisql - +import sqlite3 SQLiteSchemaGenerator = sa_base.SQLiteDDLCompiler @@ -73,10 +77,16 @@ class SQLiteHelper(SQLiteCommon): cons for cons in table.constraints if omit_uniques is None or cons.name not in omit_uniques ]) + tup = sqlite3.sqlite_version_info + if tup[0] > 3 or (tup[0] == 3 and tup[1] >= 26): + self.append('PRAGMA legacy_alter_table = ON') + self.execute() self.append('ALTER TABLE %s RENAME TO migration_tmp' % table_name) self.execute() - + if tup[0] > 3 or (tup[0] == 3 and tup[1] >= 26): + self.append('PRAGMA legacy_alter_table = OFF') + self.execute() insertion_string = self._modify_table(table, column, delta) table.create(bind=self.connection) diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/schema.py b/rhodecode/lib/dbmigrate/migrate/changeset/schema.py --- a/rhodecode/lib/dbmigrate/migrate/changeset/schema.py +++ b/rhodecode/lib/dbmigrate/migrate/changeset/schema.py @@ -1,10 +1,13 @@ """ Schema module providing common schema operations. """ +import abc +try: # Python 3 + from collections.abc import MutableMapping as DictMixin +except ImportError: # Python 2 + from UserDict import DictMixin import warnings -from UserDict import DictMixin - import sqlalchemy from sqlalchemy.schema import ForeignKeyConstraint diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/util.py b/rhodecode/lib/dbmigrate/migrate/changeset/util.py --- a/rhodecode/lib/dbmigrate/migrate/changeset/util.py +++ b/rhodecode/lib/dbmigrate/migrate/changeset/util.py @@ -1,6 +1,17 @@ """ Safe quoting method """ +from rhodecode.lib.dbmigrate.migrate.changeset import SQLA_10 + + +def fk_column_names(constraint): + if SQLA_10: + return [ + constraint.columns[key].name for key in constraint.column_keys] + else: + return [ + element.parent.name for element in constraint.elements] + def safe_quote(obj): # this is the SQLA 0.9 approach diff --git a/rhodecode/lib/dbmigrate/migrate/exceptions.py b/rhodecode/lib/dbmigrate/migrate/exceptions.py --- a/rhodecode/lib/dbmigrate/migrate/exceptions.py +++ b/rhodecode/lib/dbmigrate/migrate/exceptions.py @@ -27,6 +27,10 @@ class InvalidVersionError(ControlledSche """Invalid version number.""" +class VersionNotFoundError(KeyError): + """Specified version is not present.""" + + class DatabaseNotControlledError(ControlledSchemaError): """Database should be under version control, but it's not.""" diff --git a/rhodecode/lib/dbmigrate/schema/__init__.py b/rhodecode/lib/dbmigrate/schema/__init__.py --- a/rhodecode/lib/dbmigrate/schema/__init__.py +++ b/rhodecode/lib/dbmigrate/schema/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_1_0.py b/rhodecode/lib/dbmigrate/schema/db_1_1_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_1_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_1_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_2_0.py b/rhodecode/lib/dbmigrate/schema/db_1_2_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_2_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_2_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_4_0.py b/rhodecode/lib/dbmigrate/schema/db_1_4_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_4_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_4_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_5_0.py b/rhodecode/lib/dbmigrate/schema/db_1_5_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_5_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_5_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_5_2.py b/rhodecode/lib/dbmigrate/schema/db_1_5_2.py --- a/rhodecode/lib/dbmigrate/schema/db_1_5_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_5_2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_6_0.py b/rhodecode/lib/dbmigrate/schema/db_1_6_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_6_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_6_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_7_0.py b/rhodecode/lib/dbmigrate/schema/db_1_7_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_7_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_7_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_8_0.py b/rhodecode/lib/dbmigrate/schema/db_1_8_0.py --- a/rhodecode/lib/dbmigrate/schema/db_1_8_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_8_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_0_0.py b/rhodecode/lib/dbmigrate/schema/db_2_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_2_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_0_1.py b/rhodecode/lib/dbmigrate/schema/db_2_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_2_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_0_2.py b/rhodecode/lib/dbmigrate/schema/db_2_0_2.py --- a/rhodecode/lib/dbmigrate/schema/db_2_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_0_2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_1_0.py b/rhodecode/lib/dbmigrate/schema/db_2_1_0.py --- a/rhodecode/lib/dbmigrate/schema/db_2_1_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_1_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_2_0.py b/rhodecode/lib/dbmigrate/schema/db_2_2_0.py --- a/rhodecode/lib/dbmigrate/schema/db_2_2_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_2_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_2_3.py b/rhodecode/lib/dbmigrate/schema/db_2_2_3.py --- a/rhodecode/lib/dbmigrate/schema/db_2_2_3.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_2_3.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py b/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py b/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py b/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py --- a/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py b/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py b/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py b/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py --- a/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py @@ -0,0 +1,5547 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2010-2020 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +""" +Database Models for RhodeCode Enterprise +""" + +import re +import os +import time +import string +import hashlib +import logging +import datetime +import uuid +import warnings +import ipaddress +import functools +import traceback +import collections + +from sqlalchemy import ( + or_, and_, not_, func, cast, TypeDecorator, event, + Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column, + Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary, + Text, Float, PickleType, BigInteger) +from sqlalchemy.sql.expression import true, false, case +from sqlalchemy.sql.functions import coalesce, count # pragma: no cover +from sqlalchemy.orm import ( + relationship, joinedload, class_mapper, validates, aliased) +from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.exc import IntegrityError # pragma: no cover +from sqlalchemy.dialects.mysql import LONGTEXT +from zope.cachedescriptors.property import Lazy as LazyProperty +from pyramid import compat +from pyramid.threadlocal import get_current_request +from webhelpers2.text import remove_formatting + +from rhodecode.translation import _ +from rhodecode.lib.vcs import get_vcs_instance, VCSError +from rhodecode.lib.vcs.backends.base import EmptyCommit, Reference +from rhodecode.lib.utils2 import ( + str2bool, safe_str, get_commit_safe, safe_unicode, sha1_safe, + time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict, + glob2re, StrictAttributeDict, cleaned_uri, datetime_to_time, OrderedDefaultDict) +from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType, \ + JsonRaw +from rhodecode.lib.ext_json import json +from rhodecode.lib.caching_query import FromCache +from rhodecode.lib.encrypt import AESCipher, validate_and_get_enc_data +from rhodecode.lib.encrypt2 import Encryptor +from rhodecode.lib.exceptions import ( + ArtifactMetadataDuplicate, ArtifactMetadataBadValueType) +from rhodecode.model.meta import Base, Session + +URL_SEP = '/' +log = logging.getLogger(__name__) + +# ============================================================================= +# BASE CLASSES +# ============================================================================= + +# this is propagated from .ini file rhodecode.encrypted_values.secret or +# beaker.session.secret if first is not set. +# and initialized at environment.py +ENCRYPTION_KEY = None + +# used to sort permissions by types, '#' used here is not allowed to be in +# usernames, and it's very early in sorted string.printable table. +PERMISSION_TYPE_SORT = { + 'admin': '####', + 'write': '###', + 'read': '##', + 'none': '#', +} + + +def display_user_sort(obj): + """ + Sort function used to sort permissions in .permissions() function of + Repository, RepoGroup, UserGroup. Also it put the default user in front + of all other resources + """ + + if obj.username == User.DEFAULT_USER: + return '#####' + prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '') + return prefix + obj.username + + +def display_user_group_sort(obj): + """ + Sort function used to sort permissions in .permissions() function of + Repository, RepoGroup, UserGroup. Also it put the default user in front + of all other resources + """ + + prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '') + return prefix + obj.users_group_name + + +def _hash_key(k): + return sha1_safe(k) + + +def in_filter_generator(qry, items, limit=500): + """ + Splits IN() into multiple with OR + e.g.:: + cnt = Repository.query().filter( + or_( + *in_filter_generator(Repository.repo_id, range(100000)) + )).count() + """ + if not items: + # empty list will cause empty query which might cause security issues + # this can lead to hidden unpleasant results + items = [-1] + + parts = [] + for chunk in xrange(0, len(items), limit): + parts.append( + qry.in_(items[chunk: chunk + limit]) + ) + + return parts + + +base_table_args = { + 'extend_existing': True, + 'mysql_engine': 'InnoDB', + 'mysql_charset': 'utf8', + 'sqlite_autoincrement': True +} + + +class EncryptedTextValue(TypeDecorator): + """ + Special column for encrypted long text data, use like:: + + value = Column("encrypted_value", EncryptedValue(), nullable=False) + + This column is intelligent so if value is in unencrypted form it return + unencrypted form, but on save it always encrypts + """ + impl = Text + + def process_bind_param(self, value, dialect): + """ + Setter for storing value + """ + import rhodecode + if not value: + return value + + # protect against double encrypting if values is already encrypted + if value.startswith('enc$aes$') \ + or value.startswith('enc$aes_hmac$') \ + or value.startswith('enc2$'): + raise ValueError('value needs to be in unencrypted format, ' + 'ie. not starting with enc$ or enc2$') + + algo = rhodecode.CONFIG.get('rhodecode.encrypted_values.algorithm') or 'aes' + if algo == 'aes': + return 'enc$aes_hmac$%s' % AESCipher(ENCRYPTION_KEY, hmac=True).encrypt(value) + elif algo == 'fernet': + return Encryptor(ENCRYPTION_KEY).encrypt(value) + else: + ValueError('Bad encryption algorithm, should be fernet or aes, got: {}'.format(algo)) + + def process_result_value(self, value, dialect): + """ + Getter for retrieving value + """ + + import rhodecode + if not value: + return value + + algo = rhodecode.CONFIG.get('rhodecode.encrypted_values.algorithm') or 'aes' + enc_strict_mode = str2bool(rhodecode.CONFIG.get('rhodecode.encrypted_values.strict') or True) + if algo == 'aes': + decrypted_data = validate_and_get_enc_data(value, ENCRYPTION_KEY, enc_strict_mode) + elif algo == 'fernet': + return Encryptor(ENCRYPTION_KEY).decrypt(value) + else: + ValueError('Bad encryption algorithm, should be fernet or aes, got: {}'.format(algo)) + return decrypted_data + + +class BaseModel(object): + """ + Base Model for all classes + """ + + @classmethod + def _get_keys(cls): + """return column names for this model """ + return class_mapper(cls).c.keys() + + def get_dict(self): + """ + return dict with keys and values corresponding + to this model data """ + + d = {} + for k in self._get_keys(): + d[k] = getattr(self, k) + + # also use __json__() if present to get additional fields + _json_attr = getattr(self, '__json__', None) + if _json_attr: + # update with attributes from __json__ + if callable(_json_attr): + _json_attr = _json_attr() + for k, val in _json_attr.iteritems(): + d[k] = val + return d + + def get_appstruct(self): + """return list with keys and values tuples corresponding + to this model data """ + + lst = [] + for k in self._get_keys(): + lst.append((k, getattr(self, k),)) + return lst + + def populate_obj(self, populate_dict): + """populate model with data from given populate_dict""" + + for k in self._get_keys(): + if k in populate_dict: + setattr(self, k, populate_dict[k]) + + @classmethod + def query(cls): + return Session().query(cls) + + @classmethod + def get(cls, id_): + if id_: + return cls.query().get(id_) + + @classmethod + def get_or_404(cls, id_): + from pyramid.httpexceptions import HTTPNotFound + + try: + id_ = int(id_) + except (TypeError, ValueError): + raise HTTPNotFound() + + res = cls.query().get(id_) + if not res: + raise HTTPNotFound() + return res + + @classmethod + def getAll(cls): + # deprecated and left for backward compatibility + return cls.get_all() + + @classmethod + def get_all(cls): + return cls.query().all() + + @classmethod + def delete(cls, id_): + obj = cls.query().get(id_) + Session().delete(obj) + + @classmethod + def identity_cache(cls, session, attr_name, value): + exist_in_session = [] + for (item_cls, pkey), instance in session.identity_map.items(): + if cls == item_cls and getattr(instance, attr_name) == value: + exist_in_session.append(instance) + if exist_in_session: + if len(exist_in_session) == 1: + return exist_in_session[0] + log.exception( + 'multiple objects with attr %s and ' + 'value %s found with same name: %r', + attr_name, value, exist_in_session) + + def __repr__(self): + if hasattr(self, '__unicode__'): + # python repr needs to return str + try: + return safe_str(self.__unicode__()) + except UnicodeDecodeError: + pass + return '' % (self.__class__.__name__) + + +class RhodeCodeSetting(Base, BaseModel): + __tablename__ = 'rhodecode_settings' + __table_args__ = ( + UniqueConstraint('app_settings_name'), + base_table_args + ) + + SETTINGS_TYPES = { + 'str': safe_str, + 'int': safe_int, + 'unicode': safe_unicode, + 'bool': str2bool, + 'list': functools.partial(aslist, sep=',') + } + DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions' + GLOBAL_CONF_KEY = 'app_settings' + + app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None) + _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None) + _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None) + + def __init__(self, key='', val='', type='unicode'): + self.app_settings_name = key + self.app_settings_type = type + self.app_settings_value = val + + @validates('_app_settings_value') + def validate_settings_value(self, key, val): + assert type(val) == unicode + return val + + @hybrid_property + def app_settings_value(self): + v = self._app_settings_value + _type = self.app_settings_type + if _type: + _type = self.app_settings_type.split('.')[0] + # decode the encrypted value + if 'encrypted' in self.app_settings_type: + cipher = EncryptedTextValue() + v = safe_unicode(cipher.process_result_value(v, None)) + + converter = self.SETTINGS_TYPES.get(_type) or \ + self.SETTINGS_TYPES['unicode'] + return converter(v) + + @app_settings_value.setter + def app_settings_value(self, val): + """ + Setter that will always make sure we use unicode in app_settings_value + + :param val: + """ + val = safe_unicode(val) + # encode the encrypted value + if 'encrypted' in self.app_settings_type: + cipher = EncryptedTextValue() + val = safe_unicode(cipher.process_bind_param(val, None)) + self._app_settings_value = val + + @hybrid_property + def app_settings_type(self): + return self._app_settings_type + + @app_settings_type.setter + def app_settings_type(self, val): + if val.split('.')[0] not in self.SETTINGS_TYPES: + raise Exception('type must be one of %s got %s' + % (self.SETTINGS_TYPES.keys(), val)) + self._app_settings_type = val + + @classmethod + def get_by_prefix(cls, prefix): + return RhodeCodeSetting.query()\ + .filter(RhodeCodeSetting.app_settings_name.startswith(prefix))\ + .all() + + def __unicode__(self): + return u"<%s('%s:%s[%s]')>" % ( + self.__class__.__name__, + self.app_settings_name, self.app_settings_value, + self.app_settings_type + ) + + +class RhodeCodeUi(Base, BaseModel): + __tablename__ = 'rhodecode_ui' + __table_args__ = ( + UniqueConstraint('ui_key'), + base_table_args + ) + + HOOK_REPO_SIZE = 'changegroup.repo_size' + # HG + HOOK_PRE_PULL = 'preoutgoing.pre_pull' + HOOK_PULL = 'outgoing.pull_logger' + HOOK_PRE_PUSH = 'prechangegroup.pre_push' + HOOK_PRETX_PUSH = 'pretxnchangegroup.pre_push' + HOOK_PUSH = 'changegroup.push_logger' + HOOK_PUSH_KEY = 'pushkey.key_push' + + HOOKS_BUILTIN = [ + HOOK_PRE_PULL, + HOOK_PULL, + HOOK_PRE_PUSH, + HOOK_PRETX_PUSH, + HOOK_PUSH, + HOOK_PUSH_KEY, + ] + + # TODO: johbo: Unify way how hooks are configured for git and hg, + # git part is currently hardcoded. + + # SVN PATTERNS + SVN_BRANCH_ID = 'vcs_svn_branch' + SVN_TAG_ID = 'vcs_svn_tag' + + ui_id = Column( + "ui_id", Integer(), nullable=False, unique=True, default=None, + primary_key=True) + ui_section = Column( + "ui_section", String(255), nullable=True, unique=None, default=None) + ui_key = Column( + "ui_key", String(255), nullable=True, unique=None, default=None) + ui_value = Column( + "ui_value", String(255), nullable=True, unique=None, default=None) + ui_active = Column( + "ui_active", Boolean(), nullable=True, unique=None, default=True) + + def __repr__(self): + return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section, + self.ui_key, self.ui_value) + + +class RepoRhodeCodeSetting(Base, BaseModel): + __tablename__ = 'repo_rhodecode_settings' + __table_args__ = ( + UniqueConstraint( + 'app_settings_name', 'repository_id', + name='uq_repo_rhodecode_setting_name_repo_id'), + base_table_args + ) + + repository_id = Column( + "repository_id", Integer(), ForeignKey('repositories.repo_id'), + nullable=False) + app_settings_id = Column( + "app_settings_id", Integer(), nullable=False, unique=True, + default=None, primary_key=True) + app_settings_name = Column( + "app_settings_name", String(255), nullable=True, unique=None, + default=None) + _app_settings_value = Column( + "app_settings_value", String(4096), nullable=True, unique=None, + default=None) + _app_settings_type = Column( + "app_settings_type", String(255), nullable=True, unique=None, + default=None) + + repository = relationship('Repository') + + def __init__(self, repository_id, key='', val='', type='unicode'): + self.repository_id = repository_id + self.app_settings_name = key + self.app_settings_type = type + self.app_settings_value = val + + @validates('_app_settings_value') + def validate_settings_value(self, key, val): + assert type(val) == unicode + return val + + @hybrid_property + def app_settings_value(self): + v = self._app_settings_value + type_ = self.app_settings_type + SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES + converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode'] + return converter(v) + + @app_settings_value.setter + def app_settings_value(self, val): + """ + Setter that will always make sure we use unicode in app_settings_value + + :param val: + """ + self._app_settings_value = safe_unicode(val) + + @hybrid_property + def app_settings_type(self): + return self._app_settings_type + + @app_settings_type.setter + def app_settings_type(self, val): + SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES + if val not in SETTINGS_TYPES: + raise Exception('type must be one of %s got %s' + % (SETTINGS_TYPES.keys(), val)) + self._app_settings_type = val + + def __unicode__(self): + return u"<%s('%s:%s:%s[%s]')>" % ( + self.__class__.__name__, self.repository.repo_name, + self.app_settings_name, self.app_settings_value, + self.app_settings_type + ) + + +class RepoRhodeCodeUi(Base, BaseModel): + __tablename__ = 'repo_rhodecode_ui' + __table_args__ = ( + UniqueConstraint( + 'repository_id', 'ui_section', 'ui_key', + name='uq_repo_rhodecode_ui_repository_id_section_key'), + base_table_args + ) + + repository_id = Column( + "repository_id", Integer(), ForeignKey('repositories.repo_id'), + nullable=False) + ui_id = Column( + "ui_id", Integer(), nullable=False, unique=True, default=None, + primary_key=True) + ui_section = Column( + "ui_section", String(255), nullable=True, unique=None, default=None) + ui_key = Column( + "ui_key", String(255), nullable=True, unique=None, default=None) + ui_value = Column( + "ui_value", String(255), nullable=True, unique=None, default=None) + ui_active = Column( + "ui_active", Boolean(), nullable=True, unique=None, default=True) + + repository = relationship('Repository') + + def __repr__(self): + return '<%s[%s:%s]%s=>%s]>' % ( + self.__class__.__name__, self.repository.repo_name, + self.ui_section, self.ui_key, self.ui_value) + + +class User(Base, BaseModel): + __tablename__ = 'users' + __table_args__ = ( + UniqueConstraint('username'), UniqueConstraint('email'), + Index('u_username_idx', 'username'), + Index('u_email_idx', 'email'), + base_table_args + ) + + DEFAULT_USER = 'default' + DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org' + DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}' + + user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + username = Column("username", String(255), nullable=True, unique=None, default=None) + password = Column("password", String(255), nullable=True, unique=None, default=None) + active = Column("active", Boolean(), nullable=True, unique=None, default=True) + admin = Column("admin", Boolean(), nullable=True, unique=None, default=False) + name = Column("firstname", String(255), nullable=True, unique=None, default=None) + lastname = Column("lastname", String(255), nullable=True, unique=None, default=None) + _email = Column("email", String(255), nullable=True, unique=None, default=None) + last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None) + last_activity = Column('last_activity', DateTime(timezone=False), nullable=True, unique=None, default=None) + description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql')) + + extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None) + extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None) + _api_key = Column("api_key", String(255), nullable=True, unique=None, default=None) + inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data + + user_log = relationship('UserLog') + user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all, delete-orphan') + + repositories = relationship('Repository') + repository_groups = relationship('RepoGroup') + user_groups = relationship('UserGroup') + + user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all') + followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all') + + repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all, delete-orphan') + repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all, delete-orphan') + user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all, delete-orphan') + + group_member = relationship('UserGroupMember', cascade='all') + + notifications = relationship('UserNotification', cascade='all') + # notifications assigned to this user + user_created_notifications = relationship('Notification', cascade='all') + # comments created by this user + user_comments = relationship('ChangesetComment', cascade='all') + # user profile extra info + user_emails = relationship('UserEmailMap', cascade='all') + user_ip_map = relationship('UserIpMap', cascade='all') + user_auth_tokens = relationship('UserApiKeys', cascade='all') + user_ssh_keys = relationship('UserSshKeys', cascade='all') + + # gists + user_gists = relationship('Gist', cascade='all') + # user pull requests + user_pull_requests = relationship('PullRequest', cascade='all') + # external identities + external_identities = relationship( + 'ExternalIdentity', + primaryjoin="User.user_id==ExternalIdentity.local_user_id", + cascade='all') + # review rules + user_review_rules = relationship('RepoReviewRuleUser', cascade='all') + + # artifacts owned + artifacts = relationship('FileStore', primaryjoin='FileStore.user_id==User.user_id') + + # no cascade, set NULL + scope_artifacts = relationship('FileStore', primaryjoin='FileStore.scope_user_id==User.user_id') + + def __unicode__(self): + return u"<%s('id:%s:%s')>" % (self.__class__.__name__, + self.user_id, self.username) + + @hybrid_property + def email(self): + return self._email + + @email.setter + def email(self, val): + self._email = val.lower() if val else None + + @hybrid_property + def first_name(self): + from rhodecode.lib import helpers as h + if self.name: + return h.escape(self.name) + return self.name + + @hybrid_property + def last_name(self): + from rhodecode.lib import helpers as h + if self.lastname: + return h.escape(self.lastname) + return self.lastname + + @hybrid_property + def api_key(self): + """ + Fetch if exist an auth-token with role ALL connected to this user + """ + user_auth_token = UserApiKeys.query()\ + .filter(UserApiKeys.user_id == self.user_id)\ + .filter(or_(UserApiKeys.expires == -1, + UserApiKeys.expires >= time.time()))\ + .filter(UserApiKeys.role == UserApiKeys.ROLE_ALL).first() + if user_auth_token: + user_auth_token = user_auth_token.api_key + + return user_auth_token + + @api_key.setter + def api_key(self, val): + # don't allow to set API key this is deprecated for now + self._api_key = None + + @property + def reviewer_pull_requests(self): + return PullRequestReviewers.query() \ + .options(joinedload(PullRequestReviewers.pull_request)) \ + .filter(PullRequestReviewers.user_id == self.user_id) \ + .all() + + @property + def firstname(self): + # alias for future + return self.name + + @property + def emails(self): + other = UserEmailMap.query()\ + .filter(UserEmailMap.user == self) \ + .order_by(UserEmailMap.email_id.asc()) \ + .all() + return [self.email] + [x.email for x in other] + + def emails_cached(self): + emails = UserEmailMap.query()\ + .filter(UserEmailMap.user == self) \ + .order_by(UserEmailMap.email_id.asc()) + + emails = emails.options( + FromCache("sql_cache_short", "get_user_{}_emails".format(self.user_id)) + ) + + return [self.email] + [x.email for x in emails] + + @property + def auth_tokens(self): + auth_tokens = self.get_auth_tokens() + return [x.api_key for x in auth_tokens] + + def get_auth_tokens(self): + return UserApiKeys.query()\ + .filter(UserApiKeys.user == self)\ + .order_by(UserApiKeys.user_api_key_id.asc())\ + .all() + + @LazyProperty + def feed_token(self): + return self.get_feed_token() + + def get_feed_token(self, cache=True): + feed_tokens = UserApiKeys.query()\ + .filter(UserApiKeys.user == self)\ + .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED) + if cache: + feed_tokens = feed_tokens.options( + FromCache("sql_cache_short", "get_user_feed_token_%s" % self.user_id)) + + feed_tokens = feed_tokens.all() + if feed_tokens: + return feed_tokens[0].api_key + return 'NO_FEED_TOKEN_AVAILABLE' + + @LazyProperty + def artifact_token(self): + return self.get_artifact_token() + + def get_artifact_token(self, cache=True): + artifacts_tokens = UserApiKeys.query()\ + .filter(UserApiKeys.user == self)\ + .filter(UserApiKeys.role == UserApiKeys.ROLE_ARTIFACT_DOWNLOAD) + if cache: + artifacts_tokens = artifacts_tokens.options( + FromCache("sql_cache_short", "get_user_artifact_token_%s" % self.user_id)) + + artifacts_tokens = artifacts_tokens.all() + if artifacts_tokens: + return artifacts_tokens[0].api_key + return 'NO_ARTIFACT_TOKEN_AVAILABLE' + + @classmethod + def get(cls, user_id, cache=False): + if not user_id: + return + + user = cls.query() + if cache: + user = user.options( + FromCache("sql_cache_short", "get_users_%s" % user_id)) + return user.get(user_id) + + @classmethod + def extra_valid_auth_tokens(cls, user, role=None): + tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\ + .filter(or_(UserApiKeys.expires == -1, + UserApiKeys.expires >= time.time())) + if role: + tokens = tokens.filter(or_(UserApiKeys.role == role, + UserApiKeys.role == UserApiKeys.ROLE_ALL)) + return tokens.all() + + def authenticate_by_token(self, auth_token, roles=None, scope_repo_id=None): + from rhodecode.lib import auth + + log.debug('Trying to authenticate user: %s via auth-token, ' + 'and roles: %s', self, roles) + + if not auth_token: + return False + + roles = (roles or []) + [UserApiKeys.ROLE_ALL] + tokens_q = UserApiKeys.query()\ + .filter(UserApiKeys.user_id == self.user_id)\ + .filter(or_(UserApiKeys.expires == -1, + UserApiKeys.expires >= time.time())) + + tokens_q = tokens_q.filter(UserApiKeys.role.in_(roles)) + + crypto_backend = auth.crypto_backend() + enc_token_map = {} + plain_token_map = {} + for token in tokens_q: + if token.api_key.startswith(crypto_backend.ENC_PREF): + enc_token_map[token.api_key] = token + else: + plain_token_map[token.api_key] = token + log.debug( + 'Found %s plain and %s encrypted tokens to check for authentication for this user', + len(plain_token_map), len(enc_token_map)) + + # plain token match comes first + match = plain_token_map.get(auth_token) + + # check encrypted tokens now + if not match: + for token_hash, token in enc_token_map.items(): + # NOTE(marcink): this is expensive to calculate, but most secure + if crypto_backend.hash_check(auth_token, token_hash): + match = token + break + + if match: + log.debug('Found matching token %s', match) + if match.repo_id: + log.debug('Found scope, checking for scope match of token %s', match) + if match.repo_id == scope_repo_id: + return True + else: + log.debug( + 'AUTH_TOKEN: scope mismatch, token has a set repo scope: %s, ' + 'and calling scope is:%s, skipping further checks', + match.repo, scope_repo_id) + return False + else: + return True + + return False + + @property + def ip_addresses(self): + ret = UserIpMap.query().filter(UserIpMap.user == self).all() + return [x.ip_addr for x in ret] + + @property + def username_and_name(self): + return '%s (%s %s)' % (self.username, self.first_name, self.last_name) + + @property + def username_or_name_or_email(self): + full_name = self.full_name if self.full_name is not ' ' else None + return self.username or full_name or self.email + + @property + def full_name(self): + return '%s %s' % (self.first_name, self.last_name) + + @property + def full_name_or_username(self): + return ('%s %s' % (self.first_name, self.last_name) + if (self.first_name and self.last_name) else self.username) + + @property + def full_contact(self): + return '%s %s <%s>' % (self.first_name, self.last_name, self.email) + + @property + def short_contact(self): + return '%s %s' % (self.first_name, self.last_name) + + @property + def is_admin(self): + return self.admin + + @property + def language(self): + return self.user_data.get('language') + + def AuthUser(self, **kwargs): + """ + Returns instance of AuthUser for this user + """ + from rhodecode.lib.auth import AuthUser + return AuthUser(user_id=self.user_id, username=self.username, **kwargs) + + @hybrid_property + def user_data(self): + if not self._user_data: + return {} + + try: + return json.loads(self._user_data) + except TypeError: + return {} + + @user_data.setter + def user_data(self, val): + if not isinstance(val, dict): + raise Exception('user_data must be dict, got %s' % type(val)) + try: + self._user_data = json.dumps(val) + except Exception: + log.error(traceback.format_exc()) + + @classmethod + def get_by_username(cls, username, case_insensitive=False, + cache=False, identity_cache=False): + session = Session() + + if case_insensitive: + q = cls.query().filter( + func.lower(cls.username) == func.lower(username)) + else: + q = cls.query().filter(cls.username == username) + + if cache: + if identity_cache: + val = cls.identity_cache(session, 'username', username) + if val: + return val + else: + cache_key = "get_user_by_name_%s" % _hash_key(username) + q = q.options( + FromCache("sql_cache_short", cache_key)) + + return q.scalar() + + @classmethod + def get_by_auth_token(cls, auth_token, cache=False): + q = UserApiKeys.query()\ + .filter(UserApiKeys.api_key == auth_token)\ + .filter(or_(UserApiKeys.expires == -1, + UserApiKeys.expires >= time.time())) + if cache: + q = q.options( + FromCache("sql_cache_short", "get_auth_token_%s" % auth_token)) + + match = q.first() + if match: + return match.user + + @classmethod + def get_by_email(cls, email, case_insensitive=False, cache=False): + + if case_insensitive: + q = cls.query().filter(func.lower(cls.email) == func.lower(email)) + + else: + q = cls.query().filter(cls.email == email) + + email_key = _hash_key(email) + if cache: + q = q.options( + FromCache("sql_cache_short", "get_email_key_%s" % email_key)) + + ret = q.scalar() + if ret is None: + q = UserEmailMap.query() + # try fetching in alternate email map + if case_insensitive: + q = q.filter(func.lower(UserEmailMap.email) == func.lower(email)) + else: + q = q.filter(UserEmailMap.email == email) + q = q.options(joinedload(UserEmailMap.user)) + if cache: + q = q.options( + FromCache("sql_cache_short", "get_email_map_key_%s" % email_key)) + ret = getattr(q.scalar(), 'user', None) + + return ret + + @classmethod + def get_from_cs_author(cls, author): + """ + Tries to get User objects out of commit author string + + :param author: + """ + from rhodecode.lib.helpers import email, author_name + # Valid email in the attribute passed, see if they're in the system + _email = email(author) + if _email: + user = cls.get_by_email(_email, case_insensitive=True) + if user: + return user + # Maybe we can match by username? + _author = author_name(author) + user = cls.get_by_username(_author, case_insensitive=True) + if user: + return user + + def update_userdata(self, **kwargs): + usr = self + old = usr.user_data + old.update(**kwargs) + usr.user_data = old + Session().add(usr) + log.debug('updated userdata with %s', kwargs) + + def update_lastlogin(self): + """Update user lastlogin""" + self.last_login = datetime.datetime.now() + Session().add(self) + log.debug('updated user %s lastlogin', self.username) + + def update_password(self, new_password): + from rhodecode.lib.auth import get_crypt_password + + self.password = get_crypt_password(new_password) + Session().add(self) + + @classmethod + def get_first_super_admin(cls): + user = User.query()\ + .filter(User.admin == true()) \ + .order_by(User.user_id.asc()) \ + .first() + + if user is None: + raise Exception('FATAL: Missing administrative account!') + return user + + @classmethod + def get_all_super_admins(cls, only_active=False): + """ + Returns all admin accounts sorted by username + """ + qry = User.query().filter(User.admin == true()).order_by(User.username.asc()) + if only_active: + qry = qry.filter(User.active == true()) + return qry.all() + + @classmethod + def get_all_user_ids(cls, only_active=True): + """ + Returns all users IDs + """ + qry = Session().query(User.user_id) + + if only_active: + qry = qry.filter(User.active == true()) + return [x.user_id for x in qry] + + @classmethod + def get_default_user(cls, cache=False, refresh=False): + user = User.get_by_username(User.DEFAULT_USER, cache=cache) + if user is None: + raise Exception('FATAL: Missing default account!') + if refresh: + # The default user might be based on outdated state which + # has been loaded from the cache. + # A call to refresh() ensures that the + # latest state from the database is used. + Session().refresh(user) + return user + + def _get_default_perms(self, user, suffix=''): + from rhodecode.model.permission import PermissionModel + return PermissionModel().get_default_perms(user.user_perms, suffix) + + def get_default_perms(self, suffix=''): + return self._get_default_perms(self, suffix) + + def get_api_data(self, include_secrets=False, details='full'): + """ + Common function for generating user related data for API + + :param include_secrets: By default secrets in the API data will be replaced + by a placeholder value to prevent exposing this data by accident. In case + this data shall be exposed, set this flag to ``True``. + + :param details: details can be 'basic|full' basic gives only a subset of + the available user information that includes user_id, name and emails. + """ + user = self + user_data = self.user_data + data = { + 'user_id': user.user_id, + 'username': user.username, + 'firstname': user.name, + 'lastname': user.lastname, + 'description': user.description, + 'email': user.email, + 'emails': user.emails, + } + if details == 'basic': + return data + + auth_token_length = 40 + auth_token_replacement = '*' * auth_token_length + + extras = { + 'auth_tokens': [auth_token_replacement], + 'active': user.active, + 'admin': user.admin, + 'extern_type': user.extern_type, + 'extern_name': user.extern_name, + 'last_login': user.last_login, + 'last_activity': user.last_activity, + 'ip_addresses': user.ip_addresses, + 'language': user_data.get('language') + } + data.update(extras) + + if include_secrets: + data['auth_tokens'] = user.auth_tokens + return data + + def __json__(self): + data = { + 'full_name': self.full_name, + 'full_name_or_username': self.full_name_or_username, + 'short_contact': self.short_contact, + 'full_contact': self.full_contact, + } + data.update(self.get_api_data()) + return data + + +class UserApiKeys(Base, BaseModel): + __tablename__ = 'user_api_keys' + __table_args__ = ( + Index('uak_api_key_idx', 'api_key'), + Index('uak_api_key_expires_idx', 'api_key', 'expires'), + base_table_args + ) + __mapper_args__ = {} + + # ApiKey role + ROLE_ALL = 'token_role_all' + ROLE_HTTP = 'token_role_http' + ROLE_VCS = 'token_role_vcs' + ROLE_API = 'token_role_api' + ROLE_FEED = 'token_role_feed' + ROLE_ARTIFACT_DOWNLOAD = 'role_artifact_download' + ROLE_PASSWORD_RESET = 'token_password_reset' + + ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED, ROLE_ARTIFACT_DOWNLOAD] + + user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) + api_key = Column("api_key", String(255), nullable=False, unique=True) + description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql')) + expires = Column('expires', Float(53), nullable=False) + role = Column('role', String(255), nullable=True) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + + # scope columns + repo_id = Column( + 'repo_id', Integer(), ForeignKey('repositories.repo_id'), + nullable=True, unique=None, default=None) + repo = relationship('Repository', lazy='joined') + + repo_group_id = Column( + 'repo_group_id', Integer(), ForeignKey('groups.group_id'), + nullable=True, unique=None, default=None) + repo_group = relationship('RepoGroup', lazy='joined') + + user = relationship('User', lazy='joined') + + def __unicode__(self): + return u"<%s('%s')>" % (self.__class__.__name__, self.role) + + def __json__(self): + data = { + 'auth_token': self.api_key, + 'role': self.role, + 'scope': self.scope_humanized, + 'expired': self.expired + } + return data + + def get_api_data(self, include_secrets=False): + data = self.__json__() + if include_secrets: + return data + else: + data['auth_token'] = self.token_obfuscated + return data + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.description) + + @property + def expired(self): + if self.expires == -1: + return False + return time.time() > self.expires + + @classmethod + def _get_role_name(cls, role): + return { + cls.ROLE_ALL: _('all'), + cls.ROLE_HTTP: _('http/web interface'), + cls.ROLE_VCS: _('vcs (git/hg/svn protocol)'), + cls.ROLE_API: _('api calls'), + cls.ROLE_FEED: _('feed access'), + cls.ROLE_ARTIFACT_DOWNLOAD: _('artifacts downloads'), + }.get(role, role) + + @property + def role_humanized(self): + return self._get_role_name(self.role) + + def _get_scope(self): + if self.repo: + return 'Repository: {}'.format(self.repo.repo_name) + if self.repo_group: + return 'RepositoryGroup: {} (recursive)'.format(self.repo_group.group_name) + return 'Global' + + @property + def scope_humanized(self): + return self._get_scope() + + @property + def token_obfuscated(self): + if self.api_key: + return self.api_key[:4] + "****" + + +class UserEmailMap(Base, BaseModel): + __tablename__ = 'user_email_map' + __table_args__ = ( + Index('uem_email_idx', 'email'), + UniqueConstraint('email'), + base_table_args + ) + __mapper_args__ = {} + + email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) + _email = Column("email", String(255), nullable=True, unique=False, default=None) + user = relationship('User', lazy='joined') + + @validates('_email') + def validate_email(self, key, email): + # check if this email is not main one + main_email = Session().query(User).filter(User.email == email).scalar() + if main_email is not None: + raise AttributeError('email %s is present is user table' % email) + return email + + @hybrid_property + def email(self): + return self._email + + @email.setter + def email(self, val): + self._email = val.lower() if val else None + + +class UserIpMap(Base, BaseModel): + __tablename__ = 'user_ip_map' + __table_args__ = ( + UniqueConstraint('user_id', 'ip_addr'), + base_table_args + ) + __mapper_args__ = {} + + ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) + ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None) + active = Column("active", Boolean(), nullable=True, unique=None, default=True) + description = Column("description", String(10000), nullable=True, unique=None, default=None) + user = relationship('User', lazy='joined') + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.description) + + @classmethod + def _get_ip_range(cls, ip_addr): + net = ipaddress.ip_network(safe_unicode(ip_addr), strict=False) + return [str(net.network_address), str(net.broadcast_address)] + + def __json__(self): + return { + 'ip_addr': self.ip_addr, + 'ip_range': self._get_ip_range(self.ip_addr), + } + + def __unicode__(self): + return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__, + self.user_id, self.ip_addr) + + +class UserSshKeys(Base, BaseModel): + __tablename__ = 'user_ssh_keys' + __table_args__ = ( + Index('usk_ssh_key_fingerprint_idx', 'ssh_key_fingerprint'), + + UniqueConstraint('ssh_key_fingerprint'), + + base_table_args + ) + __mapper_args__ = {} + + ssh_key_id = Column('ssh_key_id', Integer(), nullable=False, unique=True, default=None, primary_key=True) + ssh_key_data = Column('ssh_key_data', String(10240), nullable=False, unique=None, default=None) + ssh_key_fingerprint = Column('ssh_key_fingerprint', String(255), nullable=False, unique=None, default=None) + + description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql')) + + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True, default=None) + user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) + + user = relationship('User', lazy='joined') + + def __json__(self): + data = { + 'ssh_fingerprint': self.ssh_key_fingerprint, + 'description': self.description, + 'created_on': self.created_on + } + return data + + def get_api_data(self): + data = self.__json__() + return data + + +class UserLog(Base, BaseModel): + __tablename__ = 'user_logs' + __table_args__ = ( + base_table_args, + ) + + VERSION_1 = 'v1' + VERSION_2 = 'v2' + VERSIONS = [VERSION_1, VERSION_2] + + user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id',ondelete='SET NULL'), nullable=True, unique=None, default=None) + username = Column("username", String(255), nullable=True, unique=None, default=None) + repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id', ondelete='SET NULL'), nullable=True, unique=None, default=None) + repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None) + user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None) + action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None) + action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None) + + version = Column("version", String(255), nullable=True, default=VERSION_1) + user_data = Column('user_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=LONGTEXT())))) + action_data = Column('action_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=LONGTEXT())))) + + def __unicode__(self): + return u"<%s('id:%s:%s')>" % ( + self.__class__.__name__, self.repository_name, self.action) + + def __json__(self): + return { + 'user_id': self.user_id, + 'username': self.username, + 'repository_id': self.repository_id, + 'repository_name': self.repository_name, + 'user_ip': self.user_ip, + 'action_date': self.action_date, + 'action': self.action, + } + + @hybrid_property + def entry_id(self): + return self.user_log_id + + @property + def action_as_day(self): + return datetime.date(*self.action_date.timetuple()[:3]) + + user = relationship('User') + repository = relationship('Repository', cascade='') + + +class UserGroup(Base, BaseModel): + __tablename__ = 'users_groups' + __table_args__ = ( + base_table_args, + ) + + users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None) + user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None) + users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None) + inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data + + members = relationship('UserGroupMember', cascade="all, delete-orphan", lazy="joined") + users_group_to_perm = relationship('UserGroupToPerm', cascade='all') + users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all') + users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all') + user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all') + user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all') + + user_group_review_rules = relationship('RepoReviewRuleUserGroup', cascade='all') + user = relationship('User', primaryjoin="User.user_id==UserGroup.user_id") + + @classmethod + def _load_group_data(cls, column): + if not column: + return {} + + try: + return json.loads(column) or {} + except TypeError: + return {} + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.user_group_description) + + @hybrid_property + def group_data(self): + return self._load_group_data(self._group_data) + + @group_data.expression + def group_data(self, **kwargs): + return self._group_data + + @group_data.setter + def group_data(self, val): + try: + self._group_data = json.dumps(val) + except Exception: + log.error(traceback.format_exc()) + + @classmethod + def _load_sync(cls, group_data): + if group_data: + return group_data.get('extern_type') + + @property + def sync(self): + return self._load_sync(self.group_data) + + def __unicode__(self): + return u"<%s('id:%s:%s')>" % (self.__class__.__name__, + self.users_group_id, + self.users_group_name) + + @classmethod + def get_by_group_name(cls, group_name, cache=False, + case_insensitive=False): + if case_insensitive: + q = cls.query().filter(func.lower(cls.users_group_name) == + func.lower(group_name)) + + else: + q = cls.query().filter(cls.users_group_name == group_name) + if cache: + q = q.options( + FromCache("sql_cache_short", "get_group_%s" % _hash_key(group_name))) + return q.scalar() + + @classmethod + def get(cls, user_group_id, cache=False): + if not user_group_id: + return + + user_group = cls.query() + if cache: + user_group = user_group.options( + FromCache("sql_cache_short", "get_users_group_%s" % user_group_id)) + return user_group.get(user_group_id) + + def permissions(self, with_admins=True, with_owner=True, + expand_from_user_groups=False): + """ + Permissions for user groups + """ + _admin_perm = 'usergroup.admin' + + owner_row = [] + if with_owner: + usr = AttributeDict(self.user.get_dict()) + usr.owner_row = True + usr.permission = _admin_perm + owner_row.append(usr) + + super_admin_ids = [] + super_admin_rows = [] + if with_admins: + for usr in User.get_all_super_admins(): + super_admin_ids.append(usr.user_id) + # if this admin is also owner, don't double the record + if usr.user_id == owner_row[0].user_id: + owner_row[0].admin_row = True + else: + usr = AttributeDict(usr.get_dict()) + usr.admin_row = True + usr.permission = _admin_perm + super_admin_rows.append(usr) + + q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self) + q = q.options(joinedload(UserUserGroupToPerm.user_group), + joinedload(UserUserGroupToPerm.user), + joinedload(UserUserGroupToPerm.permission),) + + # get owners and admins and permissions. We do a trick of re-writing + # objects from sqlalchemy to named-tuples due to sqlalchemy session + # has a global reference and changing one object propagates to all + # others. This means if admin is also an owner admin_row that change + # would propagate to both objects + perm_rows = [] + for _usr in q.all(): + usr = AttributeDict(_usr.user.get_dict()) + # if this user is also owner/admin, mark as duplicate record + if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids: + usr.duplicate_perm = True + usr.permission = _usr.permission.permission_name + perm_rows.append(usr) + + # filter the perm rows by 'default' first and then sort them by + # admin,write,read,none permissions sorted again alphabetically in + # each group + perm_rows = sorted(perm_rows, key=display_user_sort) + + user_groups_rows = [] + if expand_from_user_groups: + for ug in self.permission_user_groups(with_members=True): + for user_data in ug.members: + user_groups_rows.append(user_data) + + return super_admin_rows + owner_row + perm_rows + user_groups_rows + + def permission_user_groups(self, with_members=False): + q = UserGroupUserGroupToPerm.query()\ + .filter(UserGroupUserGroupToPerm.target_user_group == self) + q = q.options(joinedload(UserGroupUserGroupToPerm.user_group), + joinedload(UserGroupUserGroupToPerm.target_user_group), + joinedload(UserGroupUserGroupToPerm.permission),) + + perm_rows = [] + for _user_group in q.all(): + entry = AttributeDict(_user_group.user_group.get_dict()) + entry.permission = _user_group.permission.permission_name + if with_members: + entry.members = [x.user.get_dict() + for x in _user_group.user_group.members] + perm_rows.append(entry) + + perm_rows = sorted(perm_rows, key=display_user_group_sort) + return perm_rows + + def _get_default_perms(self, user_group, suffix=''): + from rhodecode.model.permission import PermissionModel + return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix) + + def get_default_perms(self, suffix=''): + return self._get_default_perms(self, suffix) + + def get_api_data(self, with_group_members=True, include_secrets=False): + """ + :param include_secrets: See :meth:`User.get_api_data`, this parameter is + basically forwarded. + + """ + user_group = self + data = { + 'users_group_id': user_group.users_group_id, + 'group_name': user_group.users_group_name, + 'group_description': user_group.user_group_description, + 'active': user_group.users_group_active, + 'owner': user_group.user.username, + 'sync': user_group.sync, + 'owner_email': user_group.user.email, + } + + if with_group_members: + users = [] + for user in user_group.members: + user = user.user + users.append(user.get_api_data(include_secrets=include_secrets)) + data['users'] = users + + return data + + +class UserGroupMember(Base, BaseModel): + __tablename__ = 'users_groups_members' + __table_args__ = ( + base_table_args, + ) + + users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + + user = relationship('User', lazy='joined') + users_group = relationship('UserGroup') + + def __init__(self, gr_id='', u_id=''): + self.users_group_id = gr_id + self.user_id = u_id + + +class RepositoryField(Base, BaseModel): + __tablename__ = 'repositories_fields' + __table_args__ = ( + UniqueConstraint('repository_id', 'field_key'), # no-multi field + base_table_args, + ) + + PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields + + repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) + field_key = Column("field_key", String(250)) + field_label = Column("field_label", String(1024), nullable=False) + field_value = Column("field_value", String(10000), nullable=False) + field_desc = Column("field_desc", String(1024), nullable=False) + field_type = Column("field_type", String(255), nullable=False, unique=None) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + + repository = relationship('Repository') + + @property + def field_key_prefixed(self): + return 'ex_%s' % self.field_key + + @classmethod + def un_prefix_key(cls, key): + if key.startswith(cls.PREFIX): + return key[len(cls.PREFIX):] + return key + + @classmethod + def get_by_key_name(cls, key, repo): + row = cls.query()\ + .filter(cls.repository == repo)\ + .filter(cls.field_key == key).scalar() + return row + + +class Repository(Base, BaseModel): + __tablename__ = 'repositories' + __table_args__ = ( + Index('r_repo_name_idx', 'repo_name', mysql_length=255), + base_table_args, + ) + DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}' + DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}' + DEFAULT_CLONE_URI_SSH = 'ssh://{sys_user}@{hostname}/{repo}' + + STATE_CREATED = 'repo_state_created' + STATE_PENDING = 'repo_state_pending' + STATE_ERROR = 'repo_state_error' + + LOCK_AUTOMATIC = 'lock_auto' + LOCK_API = 'lock_api' + LOCK_WEB = 'lock_web' + LOCK_PULL = 'lock_pull' + + NAME_SEP = URL_SEP + + repo_id = Column( + "repo_id", Integer(), nullable=False, unique=True, default=None, + primary_key=True) + _repo_name = Column( + "repo_name", Text(), nullable=False, default=None) + repo_name_hash = Column( + "repo_name_hash", String(255), nullable=False, unique=True) + repo_state = Column("repo_state", String(255), nullable=True) + + clone_uri = Column( + "clone_uri", EncryptedTextValue(), nullable=True, unique=False, + default=None) + push_uri = Column( + "push_uri", EncryptedTextValue(), nullable=True, unique=False, + default=None) + repo_type = Column( + "repo_type", String(255), nullable=False, unique=False, default=None) + user_id = Column( + "user_id", Integer(), ForeignKey('users.user_id'), nullable=False, + unique=False, default=None) + private = Column( + "private", Boolean(), nullable=True, unique=None, default=None) + archived = Column( + "archived", Boolean(), nullable=True, unique=None, default=None) + enable_statistics = Column( + "statistics", Boolean(), nullable=True, unique=None, default=True) + enable_downloads = Column( + "downloads", Boolean(), nullable=True, unique=None, default=True) + description = Column( + "description", String(10000), nullable=True, unique=None, default=None) + created_on = Column( + 'created_on', DateTime(timezone=False), nullable=True, unique=None, + default=datetime.datetime.now) + updated_on = Column( + 'updated_on', DateTime(timezone=False), nullable=True, unique=None, + default=datetime.datetime.now) + _landing_revision = Column( + "landing_revision", String(255), nullable=False, unique=False, + default=None) + enable_locking = Column( + "enable_locking", Boolean(), nullable=False, unique=None, + default=False) + _locked = Column( + "locked", String(255), nullable=True, unique=False, default=None) + _changeset_cache = Column( + "changeset_cache", LargeBinary(), nullable=True) # JSON data + + fork_id = Column( + "fork_id", Integer(), ForeignKey('repositories.repo_id'), + nullable=True, unique=False, default=None) + group_id = Column( + "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, + unique=False, default=None) + + user = relationship('User', lazy='joined') + fork = relationship('Repository', remote_side=repo_id, lazy='joined') + group = relationship('RepoGroup', lazy='joined') + repo_to_perm = relationship( + 'UserRepoToPerm', cascade='all', + order_by='UserRepoToPerm.repo_to_perm_id') + users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all') + stats = relationship('Statistics', cascade='all', uselist=False) + + followers = relationship( + 'UserFollowing', + primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', + cascade='all') + extra_fields = relationship( + 'RepositoryField', cascade="all, delete-orphan") + logs = relationship('UserLog') + comments = relationship( + 'ChangesetComment', cascade="all, delete-orphan") + pull_requests_source = relationship( + 'PullRequest', + primaryjoin='PullRequest.source_repo_id==Repository.repo_id', + cascade="all, delete-orphan") + pull_requests_target = relationship( + 'PullRequest', + primaryjoin='PullRequest.target_repo_id==Repository.repo_id', + cascade="all, delete-orphan") + ui = relationship('RepoRhodeCodeUi', cascade="all") + settings = relationship('RepoRhodeCodeSetting', cascade="all") + integrations = relationship('Integration', cascade="all, delete-orphan") + + scoped_tokens = relationship('UserApiKeys', cascade="all") + + # no cascade, set NULL + artifacts = relationship('FileStore', primaryjoin='FileStore.scope_repo_id==Repository.repo_id') + + def __unicode__(self): + return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id, + safe_unicode(self.repo_name)) + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.description) + + @hybrid_property + def landing_rev(self): + # always should return [rev_type, rev] + if self._landing_revision: + _rev_info = self._landing_revision.split(':') + if len(_rev_info) < 2: + _rev_info.insert(0, 'rev') + return [_rev_info[0], _rev_info[1]] + return [None, None] + + @landing_rev.setter + def landing_rev(self, val): + if ':' not in val: + raise ValueError('value must be delimited with `:` and consist ' + 'of :, got %s instead' % val) + self._landing_revision = val + + @hybrid_property + def locked(self): + if self._locked: + user_id, timelocked, reason = self._locked.split(':') + lock_values = int(user_id), timelocked, reason + else: + lock_values = [None, None, None] + return lock_values + + @locked.setter + def locked(self, val): + if val and isinstance(val, (list, tuple)): + self._locked = ':'.join(map(str, val)) + else: + self._locked = None + + @classmethod + def _load_changeset_cache(cls, repo_id, changeset_cache_raw): + from rhodecode.lib.vcs.backends.base import EmptyCommit + dummy = EmptyCommit().__json__() + if not changeset_cache_raw: + dummy['source_repo_id'] = repo_id + return json.loads(json.dumps(dummy)) + + try: + return json.loads(changeset_cache_raw) + except TypeError: + return dummy + except Exception: + log.error(traceback.format_exc()) + return dummy + + @hybrid_property + def changeset_cache(self): + return self._load_changeset_cache(self.repo_id, self._changeset_cache) + + @changeset_cache.setter + def changeset_cache(self, val): + try: + self._changeset_cache = json.dumps(val) + except Exception: + log.error(traceback.format_exc()) + + @hybrid_property + def repo_name(self): + return self._repo_name + + @repo_name.setter + def repo_name(self, value): + self._repo_name = value + self.repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest() + + @classmethod + def normalize_repo_name(cls, repo_name): + """ + Normalizes os specific repo_name to the format internally stored inside + database using URL_SEP + + :param cls: + :param repo_name: + """ + return cls.NAME_SEP.join(repo_name.split(os.sep)) + + @classmethod + def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False): + session = Session() + q = session.query(cls).filter(cls.repo_name == repo_name) + + if cache: + if identity_cache: + val = cls.identity_cache(session, 'repo_name', repo_name) + if val: + return val + else: + cache_key = "get_repo_by_name_%s" % _hash_key(repo_name) + q = q.options( + FromCache("sql_cache_short", cache_key)) + + return q.scalar() + + @classmethod + def get_by_id_or_repo_name(cls, repoid): + if isinstance(repoid, (int, long)): + try: + repo = cls.get(repoid) + except ValueError: + repo = None + else: + repo = cls.get_by_repo_name(repoid) + return repo + + @classmethod + def get_by_full_path(cls, repo_full_path): + repo_name = repo_full_path.split(cls.base_path(), 1)[-1] + repo_name = cls.normalize_repo_name(repo_name) + return cls.get_by_repo_name(repo_name.strip(URL_SEP)) + + @classmethod + def get_repo_forks(cls, repo_id): + return cls.query().filter(Repository.fork_id == repo_id) + + @classmethod + def base_path(cls): + """ + Returns base path when all repos are stored + + :param cls: + """ + q = Session().query(RhodeCodeUi)\ + .filter(RhodeCodeUi.ui_key == cls.NAME_SEP) + q = q.options(FromCache("sql_cache_short", "repository_repo_path")) + return q.one().ui_value + + @classmethod + def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None), + case_insensitive=True, archived=False): + q = Repository.query() + + if not archived: + q = q.filter(Repository.archived.isnot(true())) + + if not isinstance(user_id, Optional): + q = q.filter(Repository.user_id == user_id) + + if not isinstance(group_id, Optional): + q = q.filter(Repository.group_id == group_id) + + if case_insensitive: + q = q.order_by(func.lower(Repository.repo_name)) + else: + q = q.order_by(Repository.repo_name) + + return q.all() + + @property + def repo_uid(self): + return '_{}'.format(self.repo_id) + + @property + def forks(self): + """ + Return forks of this repo + """ + return Repository.get_repo_forks(self.repo_id) + + @property + def parent(self): + """ + Returns fork parent + """ + return self.fork + + @property + def just_name(self): + return self.repo_name.split(self.NAME_SEP)[-1] + + @property + def groups_with_parents(self): + groups = [] + if self.group is None: + return groups + + cur_gr = self.group + groups.insert(0, cur_gr) + while 1: + gr = getattr(cur_gr, 'parent_group', None) + cur_gr = cur_gr.parent_group + if gr is None: + break + groups.insert(0, gr) + + return groups + + @property + def groups_and_repo(self): + return self.groups_with_parents, self + + @LazyProperty + def repo_path(self): + """ + Returns base full path for that repository means where it actually + exists on a filesystem + """ + q = Session().query(RhodeCodeUi).filter( + RhodeCodeUi.ui_key == self.NAME_SEP) + q = q.options(FromCache("sql_cache_short", "repository_repo_path")) + return q.one().ui_value + + @property + def repo_full_path(self): + p = [self.repo_path] + # we need to split the name by / since this is how we store the + # names in the database, but that eventually needs to be converted + # into a valid system path + p += self.repo_name.split(self.NAME_SEP) + return os.path.join(*map(safe_unicode, p)) + + @property + def cache_keys(self): + """ + Returns associated cache keys for that repo + """ + invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format( + repo_id=self.repo_id) + return CacheKey.query()\ + .filter(CacheKey.cache_args == invalidation_namespace)\ + .order_by(CacheKey.cache_key)\ + .all() + + @property + def cached_diffs_relative_dir(self): + """ + Return a relative to the repository store path of cached diffs + used for safe display for users, who shouldn't know the absolute store + path + """ + return os.path.join( + os.path.dirname(self.repo_name), + self.cached_diffs_dir.split(os.path.sep)[-1]) + + @property + def cached_diffs_dir(self): + path = self.repo_full_path + return os.path.join( + os.path.dirname(path), + '.__shadow_diff_cache_repo_{}'.format(self.repo_id)) + + def cached_diffs(self): + diff_cache_dir = self.cached_diffs_dir + if os.path.isdir(diff_cache_dir): + return os.listdir(diff_cache_dir) + return [] + + def shadow_repos(self): + shadow_repos_pattern = '.__shadow_repo_{}'.format(self.repo_id) + return [ + x for x in os.listdir(os.path.dirname(self.repo_full_path)) + if x.startswith(shadow_repos_pattern)] + + def get_new_name(self, repo_name): + """ + returns new full repository name based on assigned group and new new + + :param group_name: + """ + path_prefix = self.group.full_path_splitted if self.group else [] + return self.NAME_SEP.join(path_prefix + [repo_name]) + + @property + def _config(self): + """ + Returns db based config object. + """ + from rhodecode.lib.utils import make_db_config + return make_db_config(clear_session=False, repo=self) + + def permissions(self, with_admins=True, with_owner=True, + expand_from_user_groups=False): + """ + Permissions for repositories + """ + _admin_perm = 'repository.admin' + + owner_row = [] + if with_owner: + usr = AttributeDict(self.user.get_dict()) + usr.owner_row = True + usr.permission = _admin_perm + usr.permission_id = None + owner_row.append(usr) + + super_admin_ids = [] + super_admin_rows = [] + if with_admins: + for usr in User.get_all_super_admins(): + super_admin_ids.append(usr.user_id) + # if this admin is also owner, don't double the record + if usr.user_id == owner_row[0].user_id: + owner_row[0].admin_row = True + else: + usr = AttributeDict(usr.get_dict()) + usr.admin_row = True + usr.permission = _admin_perm + usr.permission_id = None + super_admin_rows.append(usr) + + q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self) + q = q.options(joinedload(UserRepoToPerm.repository), + joinedload(UserRepoToPerm.user), + joinedload(UserRepoToPerm.permission),) + + # get owners and admins and permissions. We do a trick of re-writing + # objects from sqlalchemy to named-tuples due to sqlalchemy session + # has a global reference and changing one object propagates to all + # others. This means if admin is also an owner admin_row that change + # would propagate to both objects + perm_rows = [] + for _usr in q.all(): + usr = AttributeDict(_usr.user.get_dict()) + # if this user is also owner/admin, mark as duplicate record + if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids: + usr.duplicate_perm = True + # also check if this permission is maybe used by branch_permissions + if _usr.branch_perm_entry: + usr.branch_rules = [x.branch_rule_id for x in _usr.branch_perm_entry] + + usr.permission = _usr.permission.permission_name + usr.permission_id = _usr.repo_to_perm_id + perm_rows.append(usr) + + # filter the perm rows by 'default' first and then sort them by + # admin,write,read,none permissions sorted again alphabetically in + # each group + perm_rows = sorted(perm_rows, key=display_user_sort) + + user_groups_rows = [] + if expand_from_user_groups: + for ug in self.permission_user_groups(with_members=True): + for user_data in ug.members: + user_groups_rows.append(user_data) + + return super_admin_rows + owner_row + perm_rows + user_groups_rows + + def permission_user_groups(self, with_members=True): + q = UserGroupRepoToPerm.query()\ + .filter(UserGroupRepoToPerm.repository == self) + q = q.options(joinedload(UserGroupRepoToPerm.repository), + joinedload(UserGroupRepoToPerm.users_group), + joinedload(UserGroupRepoToPerm.permission),) + + perm_rows = [] + for _user_group in q.all(): + entry = AttributeDict(_user_group.users_group.get_dict()) + entry.permission = _user_group.permission.permission_name + if with_members: + entry.members = [x.user.get_dict() + for x in _user_group.users_group.members] + perm_rows.append(entry) + + perm_rows = sorted(perm_rows, key=display_user_group_sort) + return perm_rows + + def get_api_data(self, include_secrets=False): + """ + Common function for generating repo api data + + :param include_secrets: See :meth:`User.get_api_data`. + + """ + # TODO: mikhail: Here there is an anti-pattern, we probably need to + # move this methods on models level. + from rhodecode.model.settings import SettingsModel + from rhodecode.model.repo import RepoModel + + repo = self + _user_id, _time, _reason = self.locked + + data = { + 'repo_id': repo.repo_id, + 'repo_name': repo.repo_name, + 'repo_type': repo.repo_type, + 'clone_uri': repo.clone_uri or '', + 'push_uri': repo.push_uri or '', + 'url': RepoModel().get_url(self), + 'private': repo.private, + 'created_on': repo.created_on, + 'description': repo.description_safe, + 'landing_rev': repo.landing_rev, + 'owner': repo.user.username, + 'fork_of': repo.fork.repo_name if repo.fork else None, + 'fork_of_id': repo.fork.repo_id if repo.fork else None, + 'enable_statistics': repo.enable_statistics, + 'enable_locking': repo.enable_locking, + 'enable_downloads': repo.enable_downloads, + 'last_changeset': repo.changeset_cache, + 'locked_by': User.get(_user_id).get_api_data( + include_secrets=include_secrets) if _user_id else None, + 'locked_date': time_to_datetime(_time) if _time else None, + 'lock_reason': _reason if _reason else None, + } + + # TODO: mikhail: should be per-repo settings here + rc_config = SettingsModel().get_all_settings() + repository_fields = str2bool( + rc_config.get('rhodecode_repository_fields')) + if repository_fields: + for f in self.extra_fields: + data[f.field_key_prefixed] = f.field_value + + return data + + @classmethod + def lock(cls, repo, user_id, lock_time=None, lock_reason=None): + if not lock_time: + lock_time = time.time() + if not lock_reason: + lock_reason = cls.LOCK_AUTOMATIC + repo.locked = [user_id, lock_time, lock_reason] + Session().add(repo) + Session().commit() + + @classmethod + def unlock(cls, repo): + repo.locked = None + Session().add(repo) + Session().commit() + + @classmethod + def getlock(cls, repo): + return repo.locked + + def is_user_lock(self, user_id): + if self.lock[0]: + lock_user_id = safe_int(self.lock[0]) + user_id = safe_int(user_id) + # both are ints, and they are equal + return all([lock_user_id, user_id]) and lock_user_id == user_id + + return False + + def get_locking_state(self, action, user_id, only_when_enabled=True): + """ + Checks locking on this repository, if locking is enabled and lock is + present returns a tuple of make_lock, locked, locked_by. + make_lock can have 3 states None (do nothing) True, make lock + False release lock, This value is later propagated to hooks, which + do the locking. Think about this as signals passed to hooks what to do. + + """ + # TODO: johbo: This is part of the business logic and should be moved + # into the RepositoryModel. + + if action not in ('push', 'pull'): + raise ValueError("Invalid action value: %s" % repr(action)) + + # defines if locked error should be thrown to user + currently_locked = False + # defines if new lock should be made, tri-state + make_lock = None + repo = self + user = User.get(user_id) + + lock_info = repo.locked + + if repo and (repo.enable_locking or not only_when_enabled): + if action == 'push': + # check if it's already locked !, if it is compare users + locked_by_user_id = lock_info[0] + if user.user_id == locked_by_user_id: + log.debug( + 'Got `push` action from user %s, now unlocking', user) + # unlock if we have push from user who locked + make_lock = False + else: + # we're not the same user who locked, ban with + # code defined in settings (default is 423 HTTP Locked) ! + log.debug('Repo %s is currently locked by %s', repo, user) + currently_locked = True + elif action == 'pull': + # [0] user [1] date + if lock_info[0] and lock_info[1]: + log.debug('Repo %s is currently locked by %s', repo, user) + currently_locked = True + else: + log.debug('Setting lock on repo %s by %s', repo, user) + make_lock = True + + else: + log.debug('Repository %s do not have locking enabled', repo) + + log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s', + make_lock, currently_locked, lock_info) + + from rhodecode.lib.auth import HasRepoPermissionAny + perm_check = HasRepoPermissionAny('repository.write', 'repository.admin') + if make_lock and not perm_check(repo_name=repo.repo_name, user=user): + # if we don't have at least write permission we cannot make a lock + log.debug('lock state reset back to FALSE due to lack ' + 'of at least read permission') + make_lock = False + + return make_lock, currently_locked, lock_info + + @property + def last_commit_cache_update_diff(self): + return time.time() - (safe_int(self.changeset_cache.get('updated_on')) or 0) + + @classmethod + def _load_commit_change(cls, last_commit_cache): + from rhodecode.lib.vcs.utils.helpers import parse_datetime + empty_date = datetime.datetime.fromtimestamp(0) + date_latest = last_commit_cache.get('date', empty_date) + try: + return parse_datetime(date_latest) + except Exception: + return empty_date + + @property + def last_commit_change(self): + return self._load_commit_change(self.changeset_cache) + + @property + def last_db_change(self): + return self.updated_on + + @property + def clone_uri_hidden(self): + clone_uri = self.clone_uri + if clone_uri: + import urlobject + url_obj = urlobject.URLObject(cleaned_uri(clone_uri)) + if url_obj.password: + clone_uri = url_obj.with_password('*****') + return clone_uri + + @property + def push_uri_hidden(self): + push_uri = self.push_uri + if push_uri: + import urlobject + url_obj = urlobject.URLObject(cleaned_uri(push_uri)) + if url_obj.password: + push_uri = url_obj.with_password('*****') + return push_uri + + def clone_url(self, **override): + from rhodecode.model.settings import SettingsModel + + uri_tmpl = None + if 'with_id' in override: + uri_tmpl = self.DEFAULT_CLONE_URI_ID + del override['with_id'] + + if 'uri_tmpl' in override: + uri_tmpl = override['uri_tmpl'] + del override['uri_tmpl'] + + ssh = False + if 'ssh' in override: + ssh = True + del override['ssh'] + + # we didn't override our tmpl from **overrides + request = get_current_request() + if not uri_tmpl: + if hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'): + rc_config = request.call_context.rc_config + else: + rc_config = SettingsModel().get_all_settings(cache=True) + + if ssh: + uri_tmpl = rc_config.get( + 'rhodecode_clone_uri_ssh_tmpl') or self.DEFAULT_CLONE_URI_SSH + + else: + uri_tmpl = rc_config.get( + 'rhodecode_clone_uri_tmpl') or self.DEFAULT_CLONE_URI + + return get_clone_url(request=request, + uri_tmpl=uri_tmpl, + repo_name=self.repo_name, + repo_id=self.repo_id, + repo_type=self.repo_type, + **override) + + def set_state(self, state): + self.repo_state = state + Session().add(self) + #========================================================================== + # SCM PROPERTIES + #========================================================================== + + def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, maybe_unreachable=False): + return get_commit_safe( + self.scm_instance(), commit_id, commit_idx, pre_load=pre_load, + maybe_unreachable=maybe_unreachable) + + def get_changeset(self, rev=None, pre_load=None): + warnings.warn("Use get_commit", DeprecationWarning) + commit_id = None + commit_idx = None + if isinstance(rev, compat.string_types): + commit_id = rev + else: + commit_idx = rev + return self.get_commit(commit_id=commit_id, commit_idx=commit_idx, + pre_load=pre_load) + + def get_landing_commit(self): + """ + Returns landing commit, or if that doesn't exist returns the tip + """ + _rev_type, _rev = self.landing_rev + commit = self.get_commit(_rev) + if isinstance(commit, EmptyCommit): + return self.get_commit() + return commit + + def flush_commit_cache(self): + self.update_commit_cache(cs_cache={'raw_id':'0'}) + self.update_commit_cache() + + def update_commit_cache(self, cs_cache=None, config=None): + """ + Update cache of last commit for repository + cache_keys should be:: + + source_repo_id + short_id + raw_id + revision + parents + message + date + author + updated_on + + """ + from rhodecode.lib.vcs.backends.base import BaseChangeset + from rhodecode.lib.vcs.utils.helpers import parse_datetime + empty_date = datetime.datetime.fromtimestamp(0) + + if cs_cache is None: + # use no-cache version here + try: + scm_repo = self.scm_instance(cache=False, config=config) + except VCSError: + scm_repo = None + empty = scm_repo is None or scm_repo.is_empty() + + if not empty: + cs_cache = scm_repo.get_commit( + pre_load=["author", "date", "message", "parents", "branch"]) + else: + cs_cache = EmptyCommit() + + if isinstance(cs_cache, BaseChangeset): + cs_cache = cs_cache.__json__() + + def is_outdated(new_cs_cache): + if (new_cs_cache['raw_id'] != self.changeset_cache['raw_id'] or + new_cs_cache['revision'] != self.changeset_cache['revision']): + return True + return False + + # check if we have maybe already latest cached revision + if is_outdated(cs_cache) or not self.changeset_cache: + _current_datetime = datetime.datetime.utcnow() + last_change = cs_cache.get('date') or _current_datetime + # we check if last update is newer than the new value + # if yes, we use the current timestamp instead. Imagine you get + # old commit pushed 1y ago, we'd set last update 1y to ago. + last_change_timestamp = datetime_to_time(last_change) + current_timestamp = datetime_to_time(last_change) + if last_change_timestamp > current_timestamp and not empty: + cs_cache['date'] = _current_datetime + + _date_latest = parse_datetime(cs_cache.get('date') or empty_date) + cs_cache['updated_on'] = time.time() + self.changeset_cache = cs_cache + self.updated_on = last_change + Session().add(self) + Session().commit() + + else: + if empty: + cs_cache = EmptyCommit().__json__() + else: + cs_cache = self.changeset_cache + + _date_latest = parse_datetime(cs_cache.get('date') or empty_date) + + cs_cache['updated_on'] = time.time() + self.changeset_cache = cs_cache + self.updated_on = _date_latest + Session().add(self) + Session().commit() + + log.debug('updated repo `%s` with new commit cache %s, and last update_date: %s', + self.repo_name, cs_cache, _date_latest) + + @property + def tip(self): + return self.get_commit('tip') + + @property + def author(self): + return self.tip.author + + @property + def last_change(self): + return self.scm_instance().last_change + + def get_comments(self, revisions=None): + """ + Returns comments for this repository grouped by revisions + + :param revisions: filter query by revisions only + """ + cmts = ChangesetComment.query()\ + .filter(ChangesetComment.repo == self) + if revisions: + cmts = cmts.filter(ChangesetComment.revision.in_(revisions)) + grouped = collections.defaultdict(list) + for cmt in cmts.all(): + grouped[cmt.revision].append(cmt) + return grouped + + def statuses(self, revisions=None): + """ + Returns statuses for this repository + + :param revisions: list of revisions to get statuses for + """ + statuses = ChangesetStatus.query()\ + .filter(ChangesetStatus.repo == self)\ + .filter(ChangesetStatus.version == 0) + + if revisions: + # Try doing the filtering in chunks to avoid hitting limits + size = 500 + status_results = [] + for chunk in xrange(0, len(revisions), size): + status_results += statuses.filter( + ChangesetStatus.revision.in_( + revisions[chunk: chunk+size]) + ).all() + else: + status_results = statuses.all() + + grouped = {} + + # maybe we have open new pullrequest without a status? + stat = ChangesetStatus.STATUS_UNDER_REVIEW + status_lbl = ChangesetStatus.get_status_lbl(stat) + for pr in PullRequest.query().filter(PullRequest.source_repo == self).all(): + for rev in pr.revisions: + pr_id = pr.pull_request_id + pr_repo = pr.target_repo.repo_name + grouped[rev] = [stat, status_lbl, pr_id, pr_repo] + + for stat in status_results: + pr_id = pr_repo = None + if stat.pull_request: + pr_id = stat.pull_request.pull_request_id + pr_repo = stat.pull_request.target_repo.repo_name + grouped[stat.revision] = [str(stat.status), stat.status_lbl, + pr_id, pr_repo] + return grouped + + # ========================================================================== + # SCM CACHE INSTANCE + # ========================================================================== + + def scm_instance(self, **kwargs): + import rhodecode + + # Passing a config will not hit the cache currently only used + # for repo2dbmapper + config = kwargs.pop('config', None) + cache = kwargs.pop('cache', None) + vcs_full_cache = kwargs.pop('vcs_full_cache', None) + if vcs_full_cache is not None: + # allows override global config + full_cache = vcs_full_cache + else: + full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache')) + # if cache is NOT defined use default global, else we have a full + # control over cache behaviour + if cache is None and full_cache and not config: + log.debug('Initializing pure cached instance for %s', self.repo_path) + return self._get_instance_cached() + + # cache here is sent to the "vcs server" + return self._get_instance(cache=bool(cache), config=config) + + def _get_instance_cached(self): + from rhodecode.lib import rc_cache + + cache_namespace_uid = 'cache_repo_instance.{}'.format(self.repo_id) + invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format( + repo_id=self.repo_id) + region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid) + + @region.conditional_cache_on_arguments(namespace=cache_namespace_uid) + def get_instance_cached(repo_id, context_id, _cache_state_uid): + return self._get_instance(repo_state_uid=_cache_state_uid) + + # we must use thread scoped cache here, + # because each thread of gevent needs it's own not shared connection and cache + # we also alter `args` so the cache key is individual for every green thread. + inv_context_manager = rc_cache.InvalidationContext( + uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace, + thread_scoped=True) + with inv_context_manager as invalidation_context: + cache_state_uid = invalidation_context.cache_data['cache_state_uid'] + args = (self.repo_id, inv_context_manager.cache_key, cache_state_uid) + + # re-compute and store cache if we get invalidate signal + if invalidation_context.should_invalidate(): + instance = get_instance_cached.refresh(*args) + else: + instance = get_instance_cached(*args) + + log.debug('Repo instance fetched in %.4fs', inv_context_manager.compute_time) + return instance + + def _get_instance(self, cache=True, config=None, repo_state_uid=None): + log.debug('Initializing %s instance `%s` with cache flag set to: %s', + self.repo_type, self.repo_path, cache) + config = config or self._config + custom_wire = { + 'cache': cache, # controls the vcs.remote cache + 'repo_state_uid': repo_state_uid + } + repo = get_vcs_instance( + repo_path=safe_str(self.repo_full_path), + config=config, + with_wire=custom_wire, + create=False, + _vcs_alias=self.repo_type) + if repo is not None: + repo.count() # cache rebuild + return repo + + def get_shadow_repository_path(self, workspace_id): + from rhodecode.lib.vcs.backends.base import BaseRepository + shadow_repo_path = BaseRepository._get_shadow_repository_path( + self.repo_full_path, self.repo_id, workspace_id) + return shadow_repo_path + + def __json__(self): + return {'landing_rev': self.landing_rev} + + def get_dict(self): + + # Since we transformed `repo_name` to a hybrid property, we need to + # keep compatibility with the code which uses `repo_name` field. + + result = super(Repository, self).get_dict() + result['repo_name'] = result.pop('_repo_name', None) + return result + + +class RepoGroup(Base, BaseModel): + __tablename__ = 'groups' + __table_args__ = ( + UniqueConstraint('group_name', 'group_parent_id'), + base_table_args, + ) + __mapper_args__ = {'order_by': 'group_name'} + + CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups + + group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + _group_name = Column("group_name", String(255), nullable=False, unique=True, default=None) + group_name_hash = Column("repo_group_name_hash", String(1024), nullable=False, unique=False) + group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) + group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None) + enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) + personal = Column('personal', Boolean(), nullable=True, unique=None, default=None) + _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) # JSON data + + repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id') + users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all') + parent_group = relationship('RepoGroup', remote_side=group_id) + user = relationship('User') + integrations = relationship('Integration', cascade="all, delete-orphan") + + # no cascade, set NULL + scope_artifacts = relationship('FileStore', primaryjoin='FileStore.scope_repo_group_id==RepoGroup.group_id') + + def __init__(self, group_name='', parent_group=None): + self.group_name = group_name + self.parent_group = parent_group + + def __unicode__(self): + return u"<%s('id:%s:%s')>" % ( + self.__class__.__name__, self.group_id, self.group_name) + + @hybrid_property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, value): + self._group_name = value + self.group_name_hash = self.hash_repo_group_name(value) + + @classmethod + def _load_changeset_cache(cls, repo_id, changeset_cache_raw): + from rhodecode.lib.vcs.backends.base import EmptyCommit + dummy = EmptyCommit().__json__() + if not changeset_cache_raw: + dummy['source_repo_id'] = repo_id + return json.loads(json.dumps(dummy)) + + try: + return json.loads(changeset_cache_raw) + except TypeError: + return dummy + except Exception: + log.error(traceback.format_exc()) + return dummy + + @hybrid_property + def changeset_cache(self): + return self._load_changeset_cache('', self._changeset_cache) + + @changeset_cache.setter + def changeset_cache(self, val): + try: + self._changeset_cache = json.dumps(val) + except Exception: + log.error(traceback.format_exc()) + + @validates('group_parent_id') + def validate_group_parent_id(self, key, val): + """ + Check cycle references for a parent group to self + """ + if self.group_id and val: + assert val != self.group_id + + return val + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.group_description) + + @classmethod + def hash_repo_group_name(cls, repo_group_name): + val = remove_formatting(repo_group_name) + val = safe_str(val).lower() + chars = [] + for c in val: + if c not in string.ascii_letters: + c = str(ord(c)) + chars.append(c) + + return ''.join(chars) + + @classmethod + def _generate_choice(cls, repo_group): + from webhelpers2.html import literal as _literal + _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) + return repo_group.group_id, _name(repo_group.full_path_splitted) + + @classmethod + def groups_choices(cls, groups=None, show_empty_group=True): + if not groups: + groups = cls.query().all() + + repo_groups = [] + if show_empty_group: + repo_groups = [(-1, u'-- %s --' % _('No parent'))] + + repo_groups.extend([cls._generate_choice(x) for x in groups]) + + repo_groups = sorted( + repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0]) + return repo_groups + + @classmethod + def url_sep(cls): + return URL_SEP + + @classmethod + def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): + if case_insensitive: + gr = cls.query().filter(func.lower(cls.group_name) + == func.lower(group_name)) + else: + gr = cls.query().filter(cls.group_name == group_name) + if cache: + name_key = _hash_key(group_name) + gr = gr.options( + FromCache("sql_cache_short", "get_group_%s" % name_key)) + return gr.scalar() + + @classmethod + def get_user_personal_repo_group(cls, user_id): + user = User.get(user_id) + if user.username == User.DEFAULT_USER: + return None + + return cls.query()\ + .filter(cls.personal == true()) \ + .filter(cls.user == user) \ + .order_by(cls.group_id.asc()) \ + .first() + + @classmethod + def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None), + case_insensitive=True): + q = RepoGroup.query() + + if not isinstance(user_id, Optional): + q = q.filter(RepoGroup.user_id == user_id) + + if not isinstance(group_id, Optional): + q = q.filter(RepoGroup.group_parent_id == group_id) + + if case_insensitive: + q = q.order_by(func.lower(RepoGroup.group_name)) + else: + q = q.order_by(RepoGroup.group_name) + return q.all() + + @property + def parents(self, parents_recursion_limit=10): + groups = [] + if self.parent_group is None: + return groups + cur_gr = self.parent_group + groups.insert(0, cur_gr) + cnt = 0 + while 1: + cnt += 1 + gr = getattr(cur_gr, 'parent_group', None) + cur_gr = cur_gr.parent_group + if gr is None: + break + if cnt == parents_recursion_limit: + # this will prevent accidental infinit loops + log.error('more than %s parents found for group %s, stopping ' + 'recursive parent fetching', parents_recursion_limit, self) + break + + groups.insert(0, gr) + return groups + + @property + def last_commit_cache_update_diff(self): + return time.time() - (safe_int(self.changeset_cache.get('updated_on')) or 0) + + @classmethod + def _load_commit_change(cls, last_commit_cache): + from rhodecode.lib.vcs.utils.helpers import parse_datetime + empty_date = datetime.datetime.fromtimestamp(0) + date_latest = last_commit_cache.get('date', empty_date) + try: + return parse_datetime(date_latest) + except Exception: + return empty_date + + @property + def last_commit_change(self): + return self._load_commit_change(self.changeset_cache) + + @property + def last_db_change(self): + return self.updated_on + + @property + def children(self): + return RepoGroup.query().filter(RepoGroup.parent_group == self) + + @property + def name(self): + return self.group_name.split(RepoGroup.url_sep())[-1] + + @property + def full_path(self): + return self.group_name + + @property + def full_path_splitted(self): + return self.group_name.split(RepoGroup.url_sep()) + + @property + def repositories(self): + return Repository.query()\ + .filter(Repository.group == self)\ + .order_by(Repository.repo_name) + + @property + def repositories_recursive_count(self): + cnt = self.repositories.count() + + def children_count(group): + cnt = 0 + for child in group.children: + cnt += child.repositories.count() + cnt += children_count(child) + return cnt + + return cnt + children_count(self) + + def _recursive_objects(self, include_repos=True, include_groups=True): + all_ = [] + + def _get_members(root_gr): + if include_repos: + for r in root_gr.repositories: + all_.append(r) + childs = root_gr.children.all() + if childs: + for gr in childs: + if include_groups: + all_.append(gr) + _get_members(gr) + + root_group = [] + if include_groups: + root_group = [self] + + _get_members(self) + return root_group + all_ + + def recursive_groups_and_repos(self): + """ + Recursive return all groups, with repositories in those groups + """ + return self._recursive_objects() + + def recursive_groups(self): + """ + Returns all children groups for this group including children of children + """ + return self._recursive_objects(include_repos=False) + + def recursive_repos(self): + """ + Returns all children repositories for this group + """ + return self._recursive_objects(include_groups=False) + + def get_new_name(self, group_name): + """ + returns new full group name based on parent and new name + + :param group_name: + """ + path_prefix = (self.parent_group.full_path_splitted if + self.parent_group else []) + return RepoGroup.url_sep().join(path_prefix + [group_name]) + + def update_commit_cache(self, config=None): + """ + Update cache of last commit for newest repository inside this repository group. + cache_keys should be:: + + source_repo_id + short_id + raw_id + revision + parents + message + date + author + + """ + from rhodecode.lib.vcs.utils.helpers import parse_datetime + empty_date = datetime.datetime.fromtimestamp(0) + + def repo_groups_and_repos(root_gr): + for _repo in root_gr.repositories: + yield _repo + for child_group in root_gr.children.all(): + yield child_group + + latest_repo_cs_cache = {} + for obj in repo_groups_and_repos(self): + repo_cs_cache = obj.changeset_cache + date_latest = latest_repo_cs_cache.get('date', empty_date) + date_current = repo_cs_cache.get('date', empty_date) + current_timestamp = datetime_to_time(parse_datetime(date_latest)) + if current_timestamp < datetime_to_time(parse_datetime(date_current)): + latest_repo_cs_cache = repo_cs_cache + if hasattr(obj, 'repo_id'): + latest_repo_cs_cache['source_repo_id'] = obj.repo_id + else: + latest_repo_cs_cache['source_repo_id'] = repo_cs_cache.get('source_repo_id') + + _date_latest = parse_datetime(latest_repo_cs_cache.get('date') or empty_date) + + latest_repo_cs_cache['updated_on'] = time.time() + self.changeset_cache = latest_repo_cs_cache + self.updated_on = _date_latest + Session().add(self) + Session().commit() + + log.debug('updated repo group `%s` with new commit cache %s, and last update_date: %s', + self.group_name, latest_repo_cs_cache, _date_latest) + + def permissions(self, with_admins=True, with_owner=True, + expand_from_user_groups=False): + """ + Permissions for repository groups + """ + _admin_perm = 'group.admin' + + owner_row = [] + if with_owner: + usr = AttributeDict(self.user.get_dict()) + usr.owner_row = True + usr.permission = _admin_perm + owner_row.append(usr) + + super_admin_ids = [] + super_admin_rows = [] + if with_admins: + for usr in User.get_all_super_admins(): + super_admin_ids.append(usr.user_id) + # if this admin is also owner, don't double the record + if usr.user_id == owner_row[0].user_id: + owner_row[0].admin_row = True + else: + usr = AttributeDict(usr.get_dict()) + usr.admin_row = True + usr.permission = _admin_perm + super_admin_rows.append(usr) + + q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self) + q = q.options(joinedload(UserRepoGroupToPerm.group), + joinedload(UserRepoGroupToPerm.user), + joinedload(UserRepoGroupToPerm.permission),) + + # get owners and admins and permissions. We do a trick of re-writing + # objects from sqlalchemy to named-tuples due to sqlalchemy session + # has a global reference and changing one object propagates to all + # others. This means if admin is also an owner admin_row that change + # would propagate to both objects + perm_rows = [] + for _usr in q.all(): + usr = AttributeDict(_usr.user.get_dict()) + # if this user is also owner/admin, mark as duplicate record + if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids: + usr.duplicate_perm = True + usr.permission = _usr.permission.permission_name + perm_rows.append(usr) + + # filter the perm rows by 'default' first and then sort them by + # admin,write,read,none permissions sorted again alphabetically in + # each group + perm_rows = sorted(perm_rows, key=display_user_sort) + + user_groups_rows = [] + if expand_from_user_groups: + for ug in self.permission_user_groups(with_members=True): + for user_data in ug.members: + user_groups_rows.append(user_data) + + return super_admin_rows + owner_row + perm_rows + user_groups_rows + + def permission_user_groups(self, with_members=False): + q = UserGroupRepoGroupToPerm.query()\ + .filter(UserGroupRepoGroupToPerm.group == self) + q = q.options(joinedload(UserGroupRepoGroupToPerm.group), + joinedload(UserGroupRepoGroupToPerm.users_group), + joinedload(UserGroupRepoGroupToPerm.permission),) + + perm_rows = [] + for _user_group in q.all(): + entry = AttributeDict(_user_group.users_group.get_dict()) + entry.permission = _user_group.permission.permission_name + if with_members: + entry.members = [x.user.get_dict() + for x in _user_group.users_group.members] + perm_rows.append(entry) + + perm_rows = sorted(perm_rows, key=display_user_group_sort) + return perm_rows + + def get_api_data(self): + """ + Common function for generating api data + + """ + group = self + data = { + 'group_id': group.group_id, + 'group_name': group.group_name, + 'group_description': group.description_safe, + 'parent_group': group.parent_group.group_name if group.parent_group else None, + 'repositories': [x.repo_name for x in group.repositories], + 'owner': group.user.username, + } + return data + + def get_dict(self): + # Since we transformed `group_name` to a hybrid property, we need to + # keep compatibility with the code which uses `group_name` field. + result = super(RepoGroup, self).get_dict() + result['group_name'] = result.pop('_group_name', None) + return result + + +class Permission(Base, BaseModel): + __tablename__ = 'permissions' + __table_args__ = ( + Index('p_perm_name_idx', 'permission_name'), + base_table_args, + ) + + PERMS = [ + ('hg.admin', _('RhodeCode Super Administrator')), + + ('repository.none', _('Repository no access')), + ('repository.read', _('Repository read access')), + ('repository.write', _('Repository write access')), + ('repository.admin', _('Repository admin access')), + + ('group.none', _('Repository group no access')), + ('group.read', _('Repository group read access')), + ('group.write', _('Repository group write access')), + ('group.admin', _('Repository group admin access')), + + ('usergroup.none', _('User group no access')), + ('usergroup.read', _('User group read access')), + ('usergroup.write', _('User group write access')), + ('usergroup.admin', _('User group admin access')), + + ('branch.none', _('Branch no permissions')), + ('branch.merge', _('Branch access by web merge')), + ('branch.push', _('Branch access by push')), + ('branch.push_force', _('Branch access by push with force')), + + ('hg.repogroup.create.false', _('Repository Group creation disabled')), + ('hg.repogroup.create.true', _('Repository Group creation enabled')), + + ('hg.usergroup.create.false', _('User Group creation disabled')), + ('hg.usergroup.create.true', _('User Group creation enabled')), + + ('hg.create.none', _('Repository creation disabled')), + ('hg.create.repository', _('Repository creation enabled')), + ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')), + ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')), + + ('hg.fork.none', _('Repository forking disabled')), + ('hg.fork.repository', _('Repository forking enabled')), + + ('hg.register.none', _('Registration disabled')), + ('hg.register.manual_activate', _('User Registration with manual account activation')), + ('hg.register.auto_activate', _('User Registration with automatic account activation')), + + ('hg.password_reset.enabled', _('Password reset enabled')), + ('hg.password_reset.hidden', _('Password reset hidden')), + ('hg.password_reset.disabled', _('Password reset disabled')), + + ('hg.extern_activate.manual', _('Manual activation of external account')), + ('hg.extern_activate.auto', _('Automatic activation of external account')), + + ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')), + ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')), + ] + + # definition of system default permissions for DEFAULT user, created on + # system setup + DEFAULT_USER_PERMISSIONS = [ + # object perms + 'repository.read', + 'group.read', + 'usergroup.read', + # branch, for backward compat we need same value as before so forced pushed + 'branch.push_force', + # global + 'hg.create.repository', + 'hg.repogroup.create.false', + 'hg.usergroup.create.false', + 'hg.create.write_on_repogroup.true', + 'hg.fork.repository', + 'hg.register.manual_activate', + 'hg.password_reset.enabled', + 'hg.extern_activate.auto', + 'hg.inherit_default_perms.true', + ] + + # defines which permissions are more important higher the more important + # Weight defines which permissions are more important. + # The higher number the more important. + PERM_WEIGHTS = { + 'repository.none': 0, + 'repository.read': 1, + 'repository.write': 3, + 'repository.admin': 4, + + 'group.none': 0, + 'group.read': 1, + 'group.write': 3, + 'group.admin': 4, + + 'usergroup.none': 0, + 'usergroup.read': 1, + 'usergroup.write': 3, + 'usergroup.admin': 4, + + 'branch.none': 0, + 'branch.merge': 1, + 'branch.push': 3, + 'branch.push_force': 4, + + 'hg.repogroup.create.false': 0, + 'hg.repogroup.create.true': 1, + + 'hg.usergroup.create.false': 0, + 'hg.usergroup.create.true': 1, + + 'hg.fork.none': 0, + 'hg.fork.repository': 1, + 'hg.create.none': 0, + 'hg.create.repository': 1 + } + + permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None) + permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None) + + def __unicode__(self): + return u"<%s('%s:%s')>" % ( + self.__class__.__name__, self.permission_id, self.permission_name + ) + + @classmethod + def get_by_key(cls, key): + return cls.query().filter(cls.permission_name == key).scalar() + + @classmethod + def get_default_repo_perms(cls, user_id, repo_id=None): + q = Session().query(UserRepoToPerm, Repository, Permission)\ + .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\ + .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\ + .filter(UserRepoToPerm.user_id == user_id) + if repo_id: + q = q.filter(UserRepoToPerm.repository_id == repo_id) + return q.all() + + @classmethod + def get_default_repo_branch_perms(cls, user_id, repo_id=None): + q = Session().query(UserToRepoBranchPermission, UserRepoToPerm, Permission) \ + .join( + Permission, + UserToRepoBranchPermission.permission_id == Permission.permission_id) \ + .join( + UserRepoToPerm, + UserToRepoBranchPermission.rule_to_perm_id == UserRepoToPerm.repo_to_perm_id) \ + .filter(UserRepoToPerm.user_id == user_id) + + if repo_id: + q = q.filter(UserToRepoBranchPermission.repository_id == repo_id) + return q.order_by(UserToRepoBranchPermission.rule_order).all() + + @classmethod + def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None): + q = Session().query(UserGroupRepoToPerm, Repository, Permission)\ + .join( + Permission, + UserGroupRepoToPerm.permission_id == Permission.permission_id)\ + .join( + Repository, + UserGroupRepoToPerm.repository_id == Repository.repo_id)\ + .join( + UserGroup, + UserGroupRepoToPerm.users_group_id == + UserGroup.users_group_id)\ + .join( + UserGroupMember, + UserGroupRepoToPerm.users_group_id == + UserGroupMember.users_group_id)\ + .filter( + UserGroupMember.user_id == user_id, + UserGroup.users_group_active == true()) + if repo_id: + q = q.filter(UserGroupRepoToPerm.repository_id == repo_id) + return q.all() + + @classmethod + def get_default_repo_branch_perms_from_user_group(cls, user_id, repo_id=None): + q = Session().query(UserGroupToRepoBranchPermission, UserGroupRepoToPerm, Permission) \ + .join( + Permission, + UserGroupToRepoBranchPermission.permission_id == Permission.permission_id) \ + .join( + UserGroupRepoToPerm, + UserGroupToRepoBranchPermission.rule_to_perm_id == UserGroupRepoToPerm.users_group_to_perm_id) \ + .join( + UserGroup, + UserGroupRepoToPerm.users_group_id == UserGroup.users_group_id) \ + .join( + UserGroupMember, + UserGroupRepoToPerm.users_group_id == UserGroupMember.users_group_id) \ + .filter( + UserGroupMember.user_id == user_id, + UserGroup.users_group_active == true()) + + if repo_id: + q = q.filter(UserGroupToRepoBranchPermission.repository_id == repo_id) + return q.order_by(UserGroupToRepoBranchPermission.rule_order).all() + + @classmethod + def get_default_group_perms(cls, user_id, repo_group_id=None): + q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\ + .join( + Permission, + UserRepoGroupToPerm.permission_id == Permission.permission_id)\ + .join( + RepoGroup, + UserRepoGroupToPerm.group_id == RepoGroup.group_id)\ + .filter(UserRepoGroupToPerm.user_id == user_id) + if repo_group_id: + q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id) + return q.all() + + @classmethod + def get_default_group_perms_from_user_group( + cls, user_id, repo_group_id=None): + q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\ + .join( + Permission, + UserGroupRepoGroupToPerm.permission_id == + Permission.permission_id)\ + .join( + RepoGroup, + UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\ + .join( + UserGroup, + UserGroupRepoGroupToPerm.users_group_id == + UserGroup.users_group_id)\ + .join( + UserGroupMember, + UserGroupRepoGroupToPerm.users_group_id == + UserGroupMember.users_group_id)\ + .filter( + UserGroupMember.user_id == user_id, + UserGroup.users_group_active == true()) + if repo_group_id: + q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id) + return q.all() + + @classmethod + def get_default_user_group_perms(cls, user_id, user_group_id=None): + q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\ + .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\ + .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\ + .filter(UserUserGroupToPerm.user_id == user_id) + if user_group_id: + q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id) + return q.all() + + @classmethod + def get_default_user_group_perms_from_user_group( + cls, user_id, user_group_id=None): + TargetUserGroup = aliased(UserGroup, name='target_user_group') + q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\ + .join( + Permission, + UserGroupUserGroupToPerm.permission_id == + Permission.permission_id)\ + .join( + TargetUserGroup, + UserGroupUserGroupToPerm.target_user_group_id == + TargetUserGroup.users_group_id)\ + .join( + UserGroup, + UserGroupUserGroupToPerm.user_group_id == + UserGroup.users_group_id)\ + .join( + UserGroupMember, + UserGroupUserGroupToPerm.user_group_id == + UserGroupMember.users_group_id)\ + .filter( + UserGroupMember.user_id == user_id, + UserGroup.users_group_active == true()) + if user_group_id: + q = q.filter( + UserGroupUserGroupToPerm.user_group_id == user_group_id) + + return q.all() + + +class UserRepoToPerm(Base, BaseModel): + __tablename__ = 'repo_to_perm' + __table_args__ = ( + UniqueConstraint('user_id', 'repository_id', 'permission_id'), + base_table_args + ) + + repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) + + user = relationship('User') + repository = relationship('Repository') + permission = relationship('Permission') + + branch_perm_entry = relationship('UserToRepoBranchPermission', cascade="all, delete-orphan", lazy='joined') + + @classmethod + def create(cls, user, repository, permission): + n = cls() + n.user = user + n.repository = repository + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u'<%s => %s >' % (self.user, self.repository) + + +class UserUserGroupToPerm(Base, BaseModel): + __tablename__ = 'user_user_group_to_perm' + __table_args__ = ( + UniqueConstraint('user_id', 'user_group_id', 'permission_id'), + base_table_args + ) + + user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + + user = relationship('User') + user_group = relationship('UserGroup') + permission = relationship('Permission') + + @classmethod + def create(cls, user, user_group, permission): + n = cls() + n.user = user + n.user_group = user_group + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u'<%s => %s >' % (self.user, self.user_group) + + +class UserToPerm(Base, BaseModel): + __tablename__ = 'user_to_perm' + __table_args__ = ( + UniqueConstraint('user_id', 'permission_id'), + base_table_args + ) + + user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + + user = relationship('User') + permission = relationship('Permission', lazy='joined') + + def __unicode__(self): + return u'<%s => %s >' % (self.user, self.permission) + + +class UserGroupRepoToPerm(Base, BaseModel): + __tablename__ = 'users_group_repo_to_perm' + __table_args__ = ( + UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), + base_table_args + ) + + users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) + + users_group = relationship('UserGroup') + permission = relationship('Permission') + repository = relationship('Repository') + user_group_branch_perms = relationship('UserGroupToRepoBranchPermission', cascade='all') + + @classmethod + def create(cls, users_group, repository, permission): + n = cls() + n.users_group = users_group + n.repository = repository + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u' %s >' % (self.users_group, self.repository) + + +class UserGroupUserGroupToPerm(Base, BaseModel): + __tablename__ = 'user_group_user_group_to_perm' + __table_args__ = ( + UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'), + CheckConstraint('target_user_group_id != user_group_id'), + base_table_args + ) + + 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) + target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + + target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id') + user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id') + permission = relationship('Permission') + + @classmethod + def create(cls, target_user_group, user_group, permission): + n = cls() + n.target_user_group = target_user_group + n.user_group = user_group + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u' %s >' % (self.target_user_group, self.user_group) + + +class UserGroupToPerm(Base, BaseModel): + __tablename__ = 'users_group_to_perm' + __table_args__ = ( + UniqueConstraint('users_group_id', 'permission_id',), + base_table_args + ) + + users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + + users_group = relationship('UserGroup') + permission = relationship('Permission') + + +class UserRepoGroupToPerm(Base, BaseModel): + __tablename__ = 'user_repo_group_to_perm' + __table_args__ = ( + UniqueConstraint('user_id', 'group_id', 'permission_id'), + base_table_args + ) + + group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + + user = relationship('User') + group = relationship('RepoGroup') + permission = relationship('Permission') + + @classmethod + def create(cls, user, repository_group, permission): + n = cls() + n.user = user + n.group = repository_group + n.permission = permission + Session().add(n) + return n + + +class UserGroupRepoGroupToPerm(Base, BaseModel): + __tablename__ = 'users_group_repo_group_to_perm' + __table_args__ = ( + UniqueConstraint('users_group_id', 'group_id'), + base_table_args + ) + + 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) + users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + + users_group = relationship('UserGroup') + permission = relationship('Permission') + group = relationship('RepoGroup') + + @classmethod + def create(cls, user_group, repository_group, permission): + n = cls() + n.users_group = user_group + n.group = repository_group + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u' %s >' % (self.users_group, self.group) + + +class Statistics(Base, BaseModel): + __tablename__ = 'statistics' + __table_args__ = ( + base_table_args + ) + + stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None) + stat_on_revision = Column("stat_on_revision", Integer(), nullable=False) + commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data + commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data + languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data + + repository = relationship('Repository', single_parent=True) + + +class UserFollowing(Base, BaseModel): + __tablename__ = 'user_followings' + __table_args__ = ( + UniqueConstraint('user_id', 'follows_repository_id'), + UniqueConstraint('user_id', 'follows_user_id'), + base_table_args + ) + + user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None) + follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) + follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) + + user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id') + + follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id') + follows_repository = relationship('Repository', order_by='Repository.repo_name') + + @classmethod + def get_repo_followers(cls, repo_id): + return cls.query().filter(cls.follows_repo_id == repo_id) + + +class CacheKey(Base, BaseModel): + __tablename__ = 'cache_invalidation' + __table_args__ = ( + UniqueConstraint('cache_key'), + Index('key_idx', 'cache_key'), + base_table_args, + ) + + CACHE_TYPE_FEED = 'FEED' + + # namespaces used to register process/thread aware caches + REPO_INVALIDATION_NAMESPACE = 'repo_cache:{repo_id}' + SETTINGS_INVALIDATION_NAMESPACE = 'system_settings' + + cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None) + cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None) + cache_state_uid = Column("cache_state_uid", String(255), nullable=True, unique=None, default=None) + cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False) + + def __init__(self, cache_key, cache_args='', cache_state_uid=None): + self.cache_key = cache_key + self.cache_args = cache_args + self.cache_active = False + # first key should be same for all entries, since all workers should share it + self.cache_state_uid = cache_state_uid or self.generate_new_state_uid() + + def __unicode__(self): + return u"<%s('%s:%s[%s]')>" % ( + self.__class__.__name__, + self.cache_id, self.cache_key, self.cache_active) + + def _cache_key_partition(self): + prefix, repo_name, suffix = self.cache_key.partition(self.cache_args) + return prefix, repo_name, suffix + + def get_prefix(self): + """ + Try to extract prefix from existing cache key. The key could consist + of prefix, repo_name, suffix + """ + # this returns prefix, repo_name, suffix + return self._cache_key_partition()[0] + + def get_suffix(self): + """ + get suffix that might have been used in _get_cache_key to + generate self.cache_key. Only used for informational purposes + in repo_edit.mako. + """ + # prefix, repo_name, suffix + return self._cache_key_partition()[2] + + @classmethod + def generate_new_state_uid(cls, based_on=None): + if based_on: + return str(uuid.uuid5(uuid.NAMESPACE_URL, safe_str(based_on))) + else: + return str(uuid.uuid4()) + + @classmethod + def delete_all_cache(cls): + """ + Delete all cache keys from database. + Should only be run when all instances are down and all entries + thus stale. + """ + cls.query().delete() + Session().commit() + + @classmethod + def set_invalidate(cls, cache_uid, delete=False): + """ + Mark all caches of a repo as invalid in the database. + """ + + try: + qry = Session().query(cls).filter(cls.cache_args == cache_uid) + if delete: + qry.delete() + log.debug('cache objects deleted for cache args %s', + safe_str(cache_uid)) + else: + qry.update({"cache_active": False, + "cache_state_uid": cls.generate_new_state_uid()}) + log.debug('cache objects marked as invalid for cache args %s', + safe_str(cache_uid)) + + Session().commit() + except Exception: + log.exception( + 'Cache key invalidation failed for cache args %s', + safe_str(cache_uid)) + Session().rollback() + + @classmethod + def get_active_cache(cls, cache_key): + inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar() + if inv_obj: + return inv_obj + return None + + @classmethod + def get_namespace_map(cls, namespace): + return { + x.cache_key: x + for x in cls.query().filter(cls.cache_args == namespace)} + + +class ChangesetComment(Base, BaseModel): + __tablename__ = 'changeset_comments' + __table_args__ = ( + Index('cc_revision_idx', 'revision'), + base_table_args, + ) + + COMMENT_OUTDATED = u'comment_outdated' + COMMENT_TYPE_NOTE = u'note' + COMMENT_TYPE_TODO = u'todo' + COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO] + + comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) + repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) + revision = Column('revision', String(40), nullable=True) + pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True) + pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True) + line_no = Column('line_no', Unicode(10), nullable=True) + hl_lines = Column('hl_lines', Unicode(512), nullable=True) + f_path = Column('f_path', Unicode(1000), nullable=True) + user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False) + text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + renderer = Column('renderer', Unicode(64), nullable=True) + display_state = Column('display_state', Unicode(128), nullable=True) + + comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) + resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) + + resolved_comment = relationship('ChangesetComment', remote_side=comment_id, back_populates='resolved_by') + resolved_by = relationship('ChangesetComment', back_populates='resolved_comment') + + author = relationship('User', lazy='joined') + repo = relationship('Repository') + status_change = relationship('ChangesetStatus', cascade="all, delete-orphan", lazy='joined') + pull_request = relationship('PullRequest', lazy='joined') + pull_request_version = relationship('PullRequestVersion') + + @classmethod + def get_users(cls, revision=None, pull_request_id=None): + """ + Returns user associated with this ChangesetComment. ie those + who actually commented + + :param cls: + :param revision: + """ + q = Session().query(User)\ + .join(ChangesetComment.author) + if revision: + q = q.filter(cls.revision == revision) + elif pull_request_id: + q = q.filter(cls.pull_request_id == pull_request_id) + return q.all() + + @classmethod + def get_index_from_version(cls, pr_version, versions): + num_versions = [x.pull_request_version_id for x in versions] + try: + return num_versions.index(pr_version) +1 + except (IndexError, ValueError): + return + + @property + def outdated(self): + return self.display_state == self.COMMENT_OUTDATED + + def outdated_at_version(self, version): + """ + Checks if comment is outdated for given pull request version + """ + return self.outdated and self.pull_request_version_id != version + + def older_than_version(self, version): + """ + Checks if comment is made from previous version than given + """ + if version is None: + return self.pull_request_version_id is not None + + return self.pull_request_version_id < version + + @property + def resolved(self): + return self.resolved_by[0] if self.resolved_by else None + + @property + def is_todo(self): + return self.comment_type == self.COMMENT_TYPE_TODO + + @property + def is_inline(self): + return self.line_no and self.f_path + + def get_index_version(self, versions): + return self.get_index_from_version( + self.pull_request_version_id, versions) + + def __repr__(self): + if self.comment_id: + return '' % self.comment_id + else: + return '' % id(self) + + def get_api_data(self): + comment = self + data = { + 'comment_id': comment.comment_id, + 'comment_type': comment.comment_type, + 'comment_text': comment.text, + 'comment_status': comment.status_change, + 'comment_f_path': comment.f_path, + 'comment_lineno': comment.line_no, + 'comment_author': comment.author, + 'comment_created_on': comment.created_on, + 'comment_resolved_by': self.resolved + } + return data + + def __json__(self): + data = dict() + data.update(self.get_api_data()) + return data + + +class ChangesetStatus(Base, BaseModel): + __tablename__ = 'changeset_statuses' + __table_args__ = ( + Index('cs_revision_idx', 'revision'), + Index('cs_version_idx', 'version'), + UniqueConstraint('repo_id', 'revision', 'version'), + base_table_args + ) + + STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed' + STATUS_APPROVED = 'approved' + STATUS_REJECTED = 'rejected' + STATUS_UNDER_REVIEW = 'under_review' + + STATUSES = [ + (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default + (STATUS_APPROVED, _("Approved")), + (STATUS_REJECTED, _("Rejected")), + (STATUS_UNDER_REVIEW, _("Under Review")), + ] + + changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True) + repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None) + revision = Column('revision', String(40), nullable=False) + status = Column('status', String(128), nullable=False, default=DEFAULT) + changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id')) + modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now) + version = Column('version', Integer(), nullable=False, default=0) + pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True) + + author = relationship('User', lazy='joined') + repo = relationship('Repository') + comment = relationship('ChangesetComment', lazy='joined') + pull_request = relationship('PullRequest', lazy='joined') + + def __unicode__(self): + return u"<%s('%s[v%s]:%s')>" % ( + self.__class__.__name__, + self.status, self.version, self.author + ) + + @classmethod + def get_status_lbl(cls, value): + return dict(cls.STATUSES).get(value) + + @property + def status_lbl(self): + return ChangesetStatus.get_status_lbl(self.status) + + def get_api_data(self): + status = self + data = { + 'status_id': status.changeset_status_id, + 'status': status.status, + } + return data + + def __json__(self): + data = dict() + data.update(self.get_api_data()) + return data + + +class _SetState(object): + """ + Context processor allowing changing state for sensitive operation such as + pull request update or merge + """ + + def __init__(self, pull_request, pr_state, back_state=None): + self._pr = pull_request + self._org_state = back_state or pull_request.pull_request_state + self._pr_state = pr_state + self._current_state = None + + def __enter__(self): + log.debug('StateLock: entering set state context of pr %s, setting state to: `%s`', + self._pr, self._pr_state) + self.set_pr_state(self._pr_state) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_val is not None: + log.error(traceback.format_exc(exc_tb)) + return None + + self.set_pr_state(self._org_state) + log.debug('StateLock: exiting set state context of pr %s, setting state to: `%s`', + self._pr, self._org_state) + + @property + def state(self): + return self._current_state + + def set_pr_state(self, pr_state): + try: + self._pr.pull_request_state = pr_state + Session().add(self._pr) + Session().commit() + self._current_state = pr_state + except Exception: + log.exception('Failed to set PullRequest %s state to %s', self._pr, pr_state) + raise + + +class _PullRequestBase(BaseModel): + """ + Common attributes of pull request and version entries. + """ + + # .status values + STATUS_NEW = u'new' + STATUS_OPEN = u'open' + STATUS_CLOSED = u'closed' + + # available states + STATE_CREATING = u'creating' + STATE_UPDATING = u'updating' + STATE_MERGING = u'merging' + STATE_CREATED = u'created' + + title = Column('title', Unicode(255), nullable=True) + description = Column( + 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), + nullable=True) + description_renderer = Column('description_renderer', Unicode(64), nullable=True) + + # new/open/closed status of pull request (not approve/reject/etc) + status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW) + created_on = Column( + 'created_on', DateTime(timezone=False), nullable=False, + default=datetime.datetime.now) + updated_on = Column( + 'updated_on', DateTime(timezone=False), nullable=False, + default=datetime.datetime.now) + + pull_request_state = Column("pull_request_state", String(255), nullable=True) + + @declared_attr + def user_id(cls): + return Column( + "user_id", Integer(), ForeignKey('users.user_id'), nullable=False, + unique=None) + + # 500 revisions max + _revisions = Column( + 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql')) + + @declared_attr + def source_repo_id(cls): + # TODO: dan: rename column to source_repo_id + return Column( + 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'), + nullable=False) + + _source_ref = Column('org_ref', Unicode(255), nullable=False) + + @hybrid_property + def source_ref(self): + return self._source_ref + + @source_ref.setter + def source_ref(self, val): + parts = (val or '').split(':') + if len(parts) != 3: + raise ValueError( + 'Invalid reference format given: {}, expected X:Y:Z'.format(val)) + self._source_ref = safe_unicode(val) + + _target_ref = Column('other_ref', Unicode(255), nullable=False) + + @hybrid_property + def target_ref(self): + return self._target_ref + + @target_ref.setter + def target_ref(self, val): + parts = (val or '').split(':') + if len(parts) != 3: + raise ValueError( + 'Invalid reference format given: {}, expected X:Y:Z'.format(val)) + self._target_ref = safe_unicode(val) + + @declared_attr + def target_repo_id(cls): + # TODO: dan: rename column to target_repo_id + return Column( + 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'), + nullable=False) + + _shadow_merge_ref = Column('shadow_merge_ref', Unicode(255), nullable=True) + + # TODO: dan: rename column to last_merge_source_rev + _last_merge_source_rev = Column( + 'last_merge_org_rev', String(40), nullable=True) + # TODO: dan: rename column to last_merge_target_rev + _last_merge_target_rev = Column( + 'last_merge_other_rev', String(40), nullable=True) + _last_merge_status = Column('merge_status', Integer(), nullable=True) + last_merge_metadata = Column( + 'last_merge_metadata', MutationObj.as_mutable( + JsonType(dialect_map=dict(mysql=UnicodeText(16384))))) + + merge_rev = Column('merge_rev', String(40), nullable=True) + + reviewer_data = Column( + 'reviewer_data_json', MutationObj.as_mutable( + JsonType(dialect_map=dict(mysql=UnicodeText(16384))))) + + @property + def reviewer_data_json(self): + return json.dumps(self.reviewer_data) + + @property + def work_in_progress(self): + """checks if pull request is work in progress by checking the title""" + title = self.title.upper() + if re.match(r'^(\[WIP\]\s*|WIP:\s*|WIP\s+)', title): + return True + return False + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.description) + + @hybrid_property + def revisions(self): + return self._revisions.split(':') if self._revisions else [] + + @revisions.setter + def revisions(self, val): + self._revisions = u':'.join(val) + + @hybrid_property + def last_merge_status(self): + return safe_int(self._last_merge_status) + + @last_merge_status.setter + def last_merge_status(self, val): + self._last_merge_status = val + + @declared_attr + def author(cls): + return relationship('User', lazy='joined') + + @declared_attr + def source_repo(cls): + return relationship( + 'Repository', + primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__) + + @property + def source_ref_parts(self): + return self.unicode_to_reference(self.source_ref) + + @declared_attr + def target_repo(cls): + return relationship( + 'Repository', + primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__) + + @property + def target_ref_parts(self): + return self.unicode_to_reference(self.target_ref) + + @property + def shadow_merge_ref(self): + return self.unicode_to_reference(self._shadow_merge_ref) + + @shadow_merge_ref.setter + def shadow_merge_ref(self, ref): + self._shadow_merge_ref = self.reference_to_unicode(ref) + + @staticmethod + def unicode_to_reference(raw): + """ + Convert a unicode (or string) to a reference object. + If unicode evaluates to False it returns None. + """ + if raw: + refs = raw.split(':') + return Reference(*refs) + else: + return None + + @staticmethod + def reference_to_unicode(ref): + """ + Convert a reference object to unicode. + If reference is None it returns None. + """ + if ref: + return u':'.join(ref) + else: + return None + + def get_api_data(self, with_merge_state=True): + from rhodecode.model.pull_request import PullRequestModel + + pull_request = self + if with_merge_state: + merge_response, merge_status, msg = \ + PullRequestModel().merge_status(pull_request) + merge_state = { + 'status': merge_status, + 'message': safe_unicode(msg), + } + else: + merge_state = {'status': 'not_available', + 'message': 'not_available'} + + merge_data = { + 'clone_url': PullRequestModel().get_shadow_clone_url(pull_request), + 'reference': ( + pull_request.shadow_merge_ref._asdict() + if pull_request.shadow_merge_ref else None), + } + + data = { + 'pull_request_id': pull_request.pull_request_id, + 'url': PullRequestModel().get_url(pull_request), + 'title': pull_request.title, + 'description': pull_request.description, + 'status': pull_request.status, + 'state': pull_request.pull_request_state, + 'created_on': pull_request.created_on, + 'updated_on': pull_request.updated_on, + 'commit_ids': pull_request.revisions, + 'review_status': pull_request.calculated_review_status(), + 'mergeable': merge_state, + 'source': { + 'clone_url': pull_request.source_repo.clone_url(), + 'repository': pull_request.source_repo.repo_name, + 'reference': { + 'name': pull_request.source_ref_parts.name, + 'type': pull_request.source_ref_parts.type, + 'commit_id': pull_request.source_ref_parts.commit_id, + }, + }, + 'target': { + 'clone_url': pull_request.target_repo.clone_url(), + 'repository': pull_request.target_repo.repo_name, + 'reference': { + 'name': pull_request.target_ref_parts.name, + 'type': pull_request.target_ref_parts.type, + 'commit_id': pull_request.target_ref_parts.commit_id, + }, + }, + 'merge': merge_data, + 'author': pull_request.author.get_api_data(include_secrets=False, + details='basic'), + 'reviewers': [ + { + 'user': reviewer.get_api_data(include_secrets=False, + details='basic'), + 'reasons': reasons, + 'review_status': st[0][1].status if st else 'not_reviewed', + } + for obj, reviewer, reasons, mandatory, st in + pull_request.reviewers_statuses() + ] + } + + return data + + def set_state(self, pull_request_state, final_state=None): + """ + # goes from initial state to updating to initial state. + # initial state can be changed by specifying back_state= + with pull_request_obj.set_state(PullRequest.STATE_UPDATING): + pull_request.merge() + + :param pull_request_state: + :param final_state: + + """ + + return _SetState(self, pull_request_state, back_state=final_state) + + +class PullRequest(Base, _PullRequestBase): + __tablename__ = 'pull_requests' + __table_args__ = ( + base_table_args, + ) + + pull_request_id = Column( + 'pull_request_id', Integer(), nullable=False, primary_key=True) + + def __repr__(self): + if self.pull_request_id: + return '' % self.pull_request_id + else: + return '' % id(self) + + reviewers = relationship('PullRequestReviewers', cascade="all, delete-orphan") + statuses = relationship('ChangesetStatus', cascade="all, delete-orphan") + comments = relationship('ChangesetComment', cascade="all, delete-orphan") + versions = relationship('PullRequestVersion', cascade="all, delete-orphan", + lazy='dynamic') + + @classmethod + def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj, + internal_methods=None): + + class PullRequestDisplay(object): + """ + Special object wrapper for showing PullRequest data via Versions + It mimics PR object as close as possible. This is read only object + just for display + """ + + def __init__(self, attrs, internal=None): + self.attrs = attrs + # internal have priority over the given ones via attrs + self.internal = internal or ['versions'] + + def __getattr__(self, item): + if item in self.internal: + return getattr(self, item) + try: + return self.attrs[item] + except KeyError: + raise AttributeError( + '%s object has no attribute %s' % (self, item)) + + def __repr__(self): + return '' % self.attrs.get('pull_request_id') + + def versions(self): + return pull_request_obj.versions.order_by( + PullRequestVersion.pull_request_version_id).all() + + def is_closed(self): + return pull_request_obj.is_closed() + + def is_state_changing(self): + return pull_request_obj.is_state_changing() + + @property + def pull_request_version_id(self): + return getattr(pull_request_obj, 'pull_request_version_id', None) + + attrs = StrictAttributeDict(pull_request_obj.get_api_data(with_merge_state=False)) + + attrs.author = StrictAttributeDict( + pull_request_obj.author.get_api_data()) + if pull_request_obj.target_repo: + attrs.target_repo = StrictAttributeDict( + pull_request_obj.target_repo.get_api_data()) + attrs.target_repo.clone_url = pull_request_obj.target_repo.clone_url + + if pull_request_obj.source_repo: + attrs.source_repo = StrictAttributeDict( + pull_request_obj.source_repo.get_api_data()) + attrs.source_repo.clone_url = pull_request_obj.source_repo.clone_url + + attrs.source_ref_parts = pull_request_obj.source_ref_parts + attrs.target_ref_parts = pull_request_obj.target_ref_parts + attrs.revisions = pull_request_obj.revisions + + attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref + attrs.reviewer_data = org_pull_request_obj.reviewer_data + attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json + + return PullRequestDisplay(attrs, internal=internal_methods) + + def is_closed(self): + return self.status == self.STATUS_CLOSED + + def is_state_changing(self): + return self.pull_request_state != PullRequest.STATE_CREATED + + def __json__(self): + return { + 'revisions': self.revisions, + 'versions': self.versions_count + } + + def calculated_review_status(self): + from rhodecode.model.changeset_status import ChangesetStatusModel + return ChangesetStatusModel().calculated_review_status(self) + + def reviewers_statuses(self): + from rhodecode.model.changeset_status import ChangesetStatusModel + return ChangesetStatusModel().reviewers_statuses(self) + + @property + def workspace_id(self): + from rhodecode.model.pull_request import PullRequestModel + return PullRequestModel()._workspace_id(self) + + def get_shadow_repo(self): + workspace_id = self.workspace_id + shadow_repository_path = self.target_repo.get_shadow_repository_path(workspace_id) + if os.path.isdir(shadow_repository_path): + vcs_obj = self.target_repo.scm_instance() + return vcs_obj.get_shadow_instance(shadow_repository_path) + + @property + def versions_count(self): + """ + return number of versions this PR have, e.g a PR that once been + updated will have 2 versions + """ + return self.versions.count() + 1 + + +class PullRequestVersion(Base, _PullRequestBase): + __tablename__ = 'pull_request_versions' + __table_args__ = ( + base_table_args, + ) + + pull_request_version_id = Column( + 'pull_request_version_id', Integer(), nullable=False, primary_key=True) + pull_request_id = Column( + 'pull_request_id', Integer(), + ForeignKey('pull_requests.pull_request_id'), nullable=False) + pull_request = relationship('PullRequest') + + def __repr__(self): + if self.pull_request_version_id: + return '' % self.pull_request_version_id + else: + return '' % id(self) + + @property + def reviewers(self): + return self.pull_request.reviewers + + @property + def versions(self): + return self.pull_request.versions + + def is_closed(self): + # calculate from original + return self.pull_request.status == self.STATUS_CLOSED + + def is_state_changing(self): + return self.pull_request.pull_request_state != PullRequest.STATE_CREATED + + def calculated_review_status(self): + return self.pull_request.calculated_review_status() + + def reviewers_statuses(self): + return self.pull_request.reviewers_statuses() + + +class PullRequestReviewers(Base, BaseModel): + __tablename__ = 'pull_request_reviewers' + __table_args__ = ( + base_table_args, + ) + + @hybrid_property + def reasons(self): + if not self._reasons: + return [] + return self._reasons + + @reasons.setter + def reasons(self, val): + val = val or [] + if any(not isinstance(x, compat.string_types) for x in val): + raise Exception('invalid reasons type, must be list of strings') + self._reasons = val + + pull_requests_reviewers_id = Column( + 'pull_requests_reviewers_id', Integer(), nullable=False, + primary_key=True) + pull_request_id = Column( + "pull_request_id", Integer(), + ForeignKey('pull_requests.pull_request_id'), nullable=False) + user_id = Column( + "user_id", Integer(), ForeignKey('users.user_id'), nullable=True) + _reasons = Column( + 'reason', MutationList.as_mutable( + JsonType('list', dialect_map=dict(mysql=UnicodeText(16384))))) + + mandatory = Column("mandatory", Boolean(), nullable=False, default=False) + user = relationship('User') + pull_request = relationship('PullRequest') + + rule_data = Column( + 'rule_data_json', + JsonType(dialect_map=dict(mysql=UnicodeText(16384)))) + + def rule_user_group_data(self): + """ + Returns the voting user group rule data for this reviewer + """ + + if self.rule_data and 'vote_rule' in self.rule_data: + user_group_data = {} + if 'rule_user_group_entry_id' in self.rule_data: + # means a group with voting rules ! + user_group_data['id'] = self.rule_data['rule_user_group_entry_id'] + user_group_data['name'] = self.rule_data['rule_name'] + user_group_data['vote_rule'] = self.rule_data['vote_rule'] + + return user_group_data + + def __unicode__(self): + return u"<%s('id:%s')>" % (self.__class__.__name__, + self.pull_requests_reviewers_id) + + +class Notification(Base, BaseModel): + __tablename__ = 'notifications' + __table_args__ = ( + Index('notification_type_idx', 'type'), + base_table_args, + ) + + TYPE_CHANGESET_COMMENT = u'cs_comment' + TYPE_MESSAGE = u'message' + TYPE_MENTION = u'mention' + TYPE_REGISTRATION = u'registration' + TYPE_PULL_REQUEST = u'pull_request' + TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment' + TYPE_PULL_REQUEST_UPDATE = u'pull_request_update' + + notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True) + subject = Column('subject', Unicode(512), nullable=True) + body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True) + created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + type_ = Column('type', Unicode(255)) + + created_by_user = relationship('User') + notifications_to_users = relationship('UserNotification', lazy='joined', + cascade="all, delete-orphan") + + @property + def recipients(self): + return [x.user for x in UserNotification.query()\ + .filter(UserNotification.notification == self)\ + .order_by(UserNotification.user_id.asc()).all()] + + @classmethod + def create(cls, created_by, subject, body, recipients, type_=None): + if type_ is None: + type_ = Notification.TYPE_MESSAGE + + notification = cls() + notification.created_by_user = created_by + notification.subject = subject + notification.body = body + notification.type_ = type_ + notification.created_on = datetime.datetime.now() + + # For each recipient link the created notification to his account + for u in recipients: + assoc = UserNotification() + assoc.user_id = u.user_id + assoc.notification = notification + + # if created_by is inside recipients mark his notification + # as read + if u.user_id == created_by.user_id: + assoc.read = True + Session().add(assoc) + + Session().add(notification) + + return notification + + +class UserNotification(Base, BaseModel): + __tablename__ = 'user_to_notification' + __table_args__ = ( + UniqueConstraint('user_id', 'notification_id'), + base_table_args + ) + + user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True) + notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True) + read = Column('read', Boolean, default=False) + sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None) + + user = relationship('User', lazy="joined") + notification = relationship('Notification', lazy="joined", + order_by=lambda: Notification.created_on.desc(),) + + def mark_as_read(self): + self.read = True + Session().add(self) + + +class UserNotice(Base, BaseModel): + __tablename__ = 'user_notices' + __table_args__ = ( + base_table_args + ) + + NOTIFICATION_TYPE_MESSAGE = 'message' + NOTIFICATION_TYPE_NOTICE = 'notice' + + NOTIFICATION_LEVEL_INFO = 'info' + NOTIFICATION_LEVEL_WARNING = 'warning' + NOTIFICATION_LEVEL_ERROR = 'error' + + user_notice_id = Column('gist_id', Integer(), primary_key=True) + + notice_subject = Column('notice_subject', Unicode(512), nullable=True) + notice_body = Column('notice_body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True) + + notice_read = Column('notice_read', Boolean, default=False) + + notification_level = Column('notification_level', String(1024), default=NOTIFICATION_LEVEL_INFO) + notification_type = Column('notification_type', String(1024), default=NOTIFICATION_TYPE_NOTICE) + + notice_created_by = Column('notice_created_by', Integer(), ForeignKey('users.user_id'), nullable=True) + notice_created_on = Column('notice_created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + + user_id = Column('user_id', Integer(), ForeignKey('users.user_id')) + user = relationship('User', lazy="joined", primaryjoin='User.user_id==UserNotice.user_id') + + +class Gist(Base, BaseModel): + __tablename__ = 'gists' + __table_args__ = ( + Index('g_gist_access_id_idx', 'gist_access_id'), + Index('g_created_on_idx', 'created_on'), + base_table_args + ) + + GIST_PUBLIC = u'public' + GIST_PRIVATE = u'private' + DEFAULT_FILENAME = u'gistfile1.txt' + + ACL_LEVEL_PUBLIC = u'acl_public' + ACL_LEVEL_PRIVATE = u'acl_private' + + gist_id = Column('gist_id', Integer(), primary_key=True) + gist_access_id = Column('gist_access_id', Unicode(250)) + gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql')) + gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True) + gist_expires = Column('gist_expires', Float(53), nullable=False) + gist_type = Column('gist_type', Unicode(128), nullable=False) + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + acl_level = Column('acl_level', Unicode(128), nullable=True) + + owner = relationship('User') + + def __repr__(self): + return '' % (self.gist_type, self.gist_access_id) + + @hybrid_property + def description_safe(self): + from rhodecode.lib import helpers as h + return h.escape(self.gist_description) + + @classmethod + def get_or_404(cls, id_): + from pyramid.httpexceptions import HTTPNotFound + + res = cls.query().filter(cls.gist_access_id == id_).scalar() + if not res: + raise HTTPNotFound() + return res + + @classmethod + def get_by_access_id(cls, gist_access_id): + return cls.query().filter(cls.gist_access_id == gist_access_id).scalar() + + def gist_url(self): + from rhodecode.model.gist import GistModel + return GistModel().get_url(self) + + @classmethod + def base_path(cls): + """ + Returns base path when all gists are stored + + :param cls: + """ + from rhodecode.model.gist import GIST_STORE_LOC + q = Session().query(RhodeCodeUi)\ + .filter(RhodeCodeUi.ui_key == URL_SEP) + q = q.options(FromCache("sql_cache_short", "repository_repo_path")) + return os.path.join(q.one().ui_value, GIST_STORE_LOC) + + def get_api_data(self): + """ + Common function for generating gist related data for API + """ + gist = self + data = { + 'gist_id': gist.gist_id, + 'type': gist.gist_type, + 'access_id': gist.gist_access_id, + 'description': gist.gist_description, + 'url': gist.gist_url(), + 'expires': gist.gist_expires, + 'created_on': gist.created_on, + 'modified_at': gist.modified_at, + 'content': None, + 'acl_level': gist.acl_level, + } + return data + + def __json__(self): + data = dict( + ) + data.update(self.get_api_data()) + return data + # SCM functions + + def scm_instance(self, **kwargs): + """ + Get an instance of VCS Repository + + :param kwargs: + """ + from rhodecode.model.gist import GistModel + full_repo_path = os.path.join(self.base_path(), self.gist_access_id) + return get_vcs_instance( + repo_path=safe_str(full_repo_path), create=False, + _vcs_alias=GistModel.vcs_backend) + + +class ExternalIdentity(Base, BaseModel): + __tablename__ = 'external_identities' + __table_args__ = ( + Index('local_user_id_idx', 'local_user_id'), + Index('external_id_idx', 'external_id'), + base_table_args + ) + + external_id = Column('external_id', Unicode(255), default=u'', primary_key=True) + external_username = Column('external_username', Unicode(1024), default=u'') + local_user_id = Column('local_user_id', Integer(), ForeignKey('users.user_id'), primary_key=True) + provider_name = Column('provider_name', Unicode(255), default=u'', primary_key=True) + access_token = Column('access_token', String(1024), default=u'') + alt_token = Column('alt_token', String(1024), default=u'') + token_secret = Column('token_secret', String(1024), default=u'') + + @classmethod + def by_external_id_and_provider(cls, external_id, provider_name, local_user_id=None): + """ + Returns ExternalIdentity instance based on search params + + :param external_id: + :param provider_name: + :return: ExternalIdentity + """ + query = cls.query() + query = query.filter(cls.external_id == external_id) + query = query.filter(cls.provider_name == provider_name) + if local_user_id: + query = query.filter(cls.local_user_id == local_user_id) + return query.first() + + @classmethod + def user_by_external_id_and_provider(cls, external_id, provider_name): + """ + Returns User instance based on search params + + :param external_id: + :param provider_name: + :return: User + """ + query = User.query() + query = query.filter(cls.external_id == external_id) + query = query.filter(cls.provider_name == provider_name) + query = query.filter(User.user_id == cls.local_user_id) + return query.first() + + @classmethod + def by_local_user_id(cls, local_user_id): + """ + Returns all tokens for user + + :param local_user_id: + :return: ExternalIdentity + """ + query = cls.query() + query = query.filter(cls.local_user_id == local_user_id) + return query + + @classmethod + def load_provider_plugin(cls, plugin_id): + from rhodecode.authentication.base import loadplugin + _plugin_id = 'egg:rhodecode-enterprise-ee#{}'.format(plugin_id) + auth_plugin = loadplugin(_plugin_id) + return auth_plugin + + +class Integration(Base, BaseModel): + __tablename__ = 'integrations' + __table_args__ = ( + base_table_args + ) + + integration_id = Column('integration_id', Integer(), primary_key=True) + integration_type = Column('integration_type', String(255)) + enabled = Column('enabled', Boolean(), nullable=False) + name = Column('name', String(255), nullable=False) + child_repos_only = Column('child_repos_only', Boolean(), nullable=False, + default=False) + + settings = Column( + 'settings_json', MutationObj.as_mutable( + JsonType(dialect_map=dict(mysql=UnicodeText(16384))))) + repo_id = Column( + 'repo_id', Integer(), ForeignKey('repositories.repo_id'), + nullable=True, unique=None, default=None) + repo = relationship('Repository', lazy='joined') + + repo_group_id = Column( + 'repo_group_id', Integer(), ForeignKey('groups.group_id'), + nullable=True, unique=None, default=None) + repo_group = relationship('RepoGroup', lazy='joined') + + @property + def scope(self): + if self.repo: + return repr(self.repo) + if self.repo_group: + if self.child_repos_only: + return repr(self.repo_group) + ' (child repos only)' + else: + return repr(self.repo_group) + ' (recursive)' + if self.child_repos_only: + return 'root_repos' + return 'global' + + def __repr__(self): + return '' % (self.integration_type, self.scope) + + +class RepoReviewRuleUser(Base, BaseModel): + __tablename__ = 'repo_review_rules_users' + __table_args__ = ( + base_table_args + ) + + repo_review_rule_user_id = Column('repo_review_rule_user_id', Integer(), primary_key=True) + repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id')) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False) + mandatory = Column("mandatory", Boolean(), nullable=False, default=False) + user = relationship('User') + + def rule_data(self): + return { + 'mandatory': self.mandatory + } + + +class RepoReviewRuleUserGroup(Base, BaseModel): + __tablename__ = 'repo_review_rules_users_groups' + __table_args__ = ( + base_table_args + ) + + VOTE_RULE_ALL = -1 + + repo_review_rule_users_group_id = Column('repo_review_rule_users_group_id', Integer(), primary_key=True) + repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id')) + users_group_id = Column("users_group_id", Integer(),ForeignKey('users_groups.users_group_id'), nullable=False) + mandatory = Column("mandatory", Boolean(), nullable=False, default=False) + vote_rule = Column("vote_rule", Integer(), nullable=True, default=VOTE_RULE_ALL) + users_group = relationship('UserGroup') + + def rule_data(self): + return { + 'mandatory': self.mandatory, + 'vote_rule': self.vote_rule + } + + @property + def vote_rule_label(self): + if not self.vote_rule or self.vote_rule == self.VOTE_RULE_ALL: + return 'all must vote' + else: + return 'min. vote {}'.format(self.vote_rule) + + +class RepoReviewRule(Base, BaseModel): + __tablename__ = 'repo_review_rules' + __table_args__ = ( + base_table_args + ) + + repo_review_rule_id = Column( + 'repo_review_rule_id', Integer(), primary_key=True) + repo_id = Column( + "repo_id", Integer(), ForeignKey('repositories.repo_id')) + repo = relationship('Repository', backref='review_rules') + + review_rule_name = Column('review_rule_name', String(255)) + _branch_pattern = Column("branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob + _target_branch_pattern = Column("target_branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob + _file_pattern = Column("file_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob + + use_authors_for_review = Column("use_authors_for_review", Boolean(), nullable=False, default=False) + forbid_author_to_review = Column("forbid_author_to_review", Boolean(), nullable=False, default=False) + forbid_commit_author_to_review = Column("forbid_commit_author_to_review", Boolean(), nullable=False, default=False) + forbid_adding_reviewers = Column("forbid_adding_reviewers", Boolean(), nullable=False, default=False) + + rule_users = relationship('RepoReviewRuleUser') + rule_user_groups = relationship('RepoReviewRuleUserGroup') + + def _validate_pattern(self, value): + re.compile('^' + glob2re(value) + '$') + + @hybrid_property + def source_branch_pattern(self): + return self._branch_pattern or '*' + + @source_branch_pattern.setter + def source_branch_pattern(self, value): + self._validate_pattern(value) + self._branch_pattern = value or '*' + + @hybrid_property + def target_branch_pattern(self): + return self._target_branch_pattern or '*' + + @target_branch_pattern.setter + def target_branch_pattern(self, value): + self._validate_pattern(value) + self._target_branch_pattern = value or '*' + + @hybrid_property + def file_pattern(self): + return self._file_pattern or '*' + + @file_pattern.setter + def file_pattern(self, value): + self._validate_pattern(value) + self._file_pattern = value or '*' + + def matches(self, source_branch, target_branch, files_changed): + """ + Check if this review rule matches a branch/files in a pull request + + :param source_branch: source branch name for the commit + :param target_branch: target branch name for the commit + :param files_changed: list of file paths changed in the pull request + """ + + source_branch = source_branch or '' + target_branch = target_branch or '' + files_changed = files_changed or [] + + branch_matches = True + if source_branch or target_branch: + if self.source_branch_pattern == '*': + source_branch_match = True + else: + if self.source_branch_pattern.startswith('re:'): + source_pattern = self.source_branch_pattern[3:] + else: + source_pattern = '^' + glob2re(self.source_branch_pattern) + '$' + source_branch_regex = re.compile(source_pattern) + source_branch_match = bool(source_branch_regex.search(source_branch)) + if self.target_branch_pattern == '*': + target_branch_match = True + else: + if self.target_branch_pattern.startswith('re:'): + target_pattern = self.target_branch_pattern[3:] + else: + target_pattern = '^' + glob2re(self.target_branch_pattern) + '$' + target_branch_regex = re.compile(target_pattern) + target_branch_match = bool(target_branch_regex.search(target_branch)) + + branch_matches = source_branch_match and target_branch_match + + files_matches = True + if self.file_pattern != '*': + files_matches = False + if self.file_pattern.startswith('re:'): + file_pattern = self.file_pattern[3:] + else: + file_pattern = glob2re(self.file_pattern) + file_regex = re.compile(file_pattern) + for filename in files_changed: + if file_regex.search(filename): + files_matches = True + break + + return branch_matches and files_matches + + @property + def review_users(self): + """ Returns the users which this rule applies to """ + + users = collections.OrderedDict() + + for rule_user in self.rule_users: + if rule_user.user.active: + if rule_user.user not in users: + users[rule_user.user.username] = { + 'user': rule_user.user, + 'source': 'user', + 'source_data': {}, + 'data': rule_user.rule_data() + } + + for rule_user_group in self.rule_user_groups: + source_data = { + 'user_group_id': rule_user_group.users_group.users_group_id, + 'name': rule_user_group.users_group.users_group_name, + 'members': len(rule_user_group.users_group.members) + } + for member in rule_user_group.users_group.members: + if member.user.active: + key = member.user.username + if key in users: + # skip this member as we have him already + # this prevents from override the "first" matched + # users with duplicates in multiple groups + continue + + users[key] = { + 'user': member.user, + 'source': 'user_group', + 'source_data': source_data, + 'data': rule_user_group.rule_data() + } + + return users + + def user_group_vote_rule(self, user_id): + + rules = [] + if not self.rule_user_groups: + return rules + + for user_group in self.rule_user_groups: + user_group_members = [x.user_id for x in user_group.users_group.members] + if user_id in user_group_members: + rules.append(user_group) + return rules + + def __repr__(self): + return '' % ( + self.repo_review_rule_id, self.repo) + + +class ScheduleEntry(Base, BaseModel): + __tablename__ = 'schedule_entries' + __table_args__ = ( + UniqueConstraint('schedule_name', name='s_schedule_name_idx'), + UniqueConstraint('task_uid', name='s_task_uid_idx'), + base_table_args, + ) + + schedule_types = ['crontab', 'timedelta', 'integer'] + schedule_entry_id = Column('schedule_entry_id', Integer(), primary_key=True) + + schedule_name = Column("schedule_name", String(255), nullable=False, unique=None, default=None) + schedule_description = Column("schedule_description", String(10000), nullable=True, unique=None, default=None) + schedule_enabled = Column("schedule_enabled", Boolean(), nullable=False, unique=None, default=True) + + _schedule_type = Column("schedule_type", String(255), nullable=False, unique=None, default=None) + schedule_definition = Column('schedule_definition_json', MutationObj.as_mutable(JsonType(default=lambda: "", dialect_map=dict(mysql=LONGTEXT())))) + + schedule_last_run = Column('schedule_last_run', DateTime(timezone=False), nullable=True, unique=None, default=None) + schedule_total_run_count = Column('schedule_total_run_count', Integer(), nullable=True, unique=None, default=0) + + # task + task_uid = Column("task_uid", String(255), nullable=False, unique=None, default=None) + task_dot_notation = Column("task_dot_notation", String(4096), nullable=False, unique=None, default=None) + task_args = Column('task_args_json', MutationObj.as_mutable(JsonType(default=list, dialect_map=dict(mysql=LONGTEXT())))) + task_kwargs = Column('task_kwargs_json', MutationObj.as_mutable(JsonType(default=dict, dialect_map=dict(mysql=LONGTEXT())))) + + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=None) + + @hybrid_property + def schedule_type(self): + return self._schedule_type + + @schedule_type.setter + def schedule_type(self, val): + if val not in self.schedule_types: + raise ValueError('Value must be on of `{}` and got `{}`'.format( + val, self.schedule_type)) + + self._schedule_type = val + + @classmethod + def get_uid(cls, obj): + args = obj.task_args + kwargs = obj.task_kwargs + if isinstance(args, JsonRaw): + try: + args = json.loads(args) + except ValueError: + args = tuple() + + if isinstance(kwargs, JsonRaw): + try: + kwargs = json.loads(kwargs) + except ValueError: + kwargs = dict() + + dot_notation = obj.task_dot_notation + val = '.'.join(map(safe_str, [ + sorted(dot_notation), args, sorted(kwargs.items())])) + return hashlib.sha1(val).hexdigest() + + @classmethod + def get_by_schedule_name(cls, schedule_name): + return cls.query().filter(cls.schedule_name == schedule_name).scalar() + + @classmethod + def get_by_schedule_id(cls, schedule_id): + return cls.query().filter(cls.schedule_entry_id == schedule_id).scalar() + + @property + def task(self): + return self.task_dot_notation + + @property + def schedule(self): + from rhodecode.lib.celerylib.utils import raw_2_schedule + schedule = raw_2_schedule(self.schedule_definition, self.schedule_type) + return schedule + + @property + def args(self): + try: + return list(self.task_args or []) + except ValueError: + return list() + + @property + def kwargs(self): + try: + return dict(self.task_kwargs or {}) + except ValueError: + return dict() + + def _as_raw(self, val): + if hasattr(val, 'de_coerce'): + val = val.de_coerce() + if val: + val = json.dumps(val) + + return val + + @property + def schedule_definition_raw(self): + return self._as_raw(self.schedule_definition) + + @property + def args_raw(self): + return self._as_raw(self.task_args) + + @property + def kwargs_raw(self): + return self._as_raw(self.task_kwargs) + + def __repr__(self): + return ''.format( + self.schedule_entry_id, self.schedule_name) + + +@event.listens_for(ScheduleEntry, 'before_update') +def update_task_uid(mapper, connection, target): + target.task_uid = ScheduleEntry.get_uid(target) + + +@event.listens_for(ScheduleEntry, 'before_insert') +def set_task_uid(mapper, connection, target): + target.task_uid = ScheduleEntry.get_uid(target) + + +class _BaseBranchPerms(BaseModel): + @classmethod + def compute_hash(cls, value): + return sha1_safe(value) + + @hybrid_property + def branch_pattern(self): + return self._branch_pattern or '*' + + @hybrid_property + def branch_hash(self): + return self._branch_hash + + def _validate_glob(self, value): + re.compile('^' + glob2re(value) + '$') + + @branch_pattern.setter + def branch_pattern(self, value): + self._validate_glob(value) + self._branch_pattern = value or '*' + # set the Hash when setting the branch pattern + self._branch_hash = self.compute_hash(self._branch_pattern) + + def matches(self, branch): + """ + Check if this the branch matches entry + + :param branch: branch name for the commit + """ + + branch = branch or '' + + branch_matches = True + if branch: + branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$') + branch_matches = bool(branch_regex.search(branch)) + + return branch_matches + + +class UserToRepoBranchPermission(Base, _BaseBranchPerms): + __tablename__ = 'user_to_repo_branch_permissions' + __table_args__ = ( + base_table_args + ) + + branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True) + + repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) + repo = relationship('Repository', backref='user_branch_perms') + + permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + permission = relationship('Permission') + + rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('repo_to_perm.repo_to_perm_id'), nullable=False, unique=None, default=None) + user_repo_to_perm = relationship('UserRepoToPerm') + + rule_order = Column('rule_order', Integer(), nullable=False) + _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob + _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql')) + + def __unicode__(self): + return u' %r)>' % ( + self.user_repo_to_perm, self.branch_pattern) + + +class UserGroupToRepoBranchPermission(Base, _BaseBranchPerms): + __tablename__ = 'user_group_to_repo_branch_permissions' + __table_args__ = ( + base_table_args + ) + + branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True) + + repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) + repo = relationship('Repository', backref='user_group_branch_perms') + + permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + permission = relationship('Permission') + + 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) + user_group_repo_to_perm = relationship('UserGroupRepoToPerm') + + rule_order = Column('rule_order', Integer(), nullable=False) + _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob + _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql')) + + def __unicode__(self): + return u' %r)>' % ( + self.user_group_repo_to_perm, self.branch_pattern) + + +class UserBookmark(Base, BaseModel): + __tablename__ = 'user_bookmarks' + __table_args__ = ( + UniqueConstraint('user_id', 'bookmark_repo_id'), + UniqueConstraint('user_id', 'bookmark_repo_group_id'), + UniqueConstraint('user_id', 'bookmark_position'), + base_table_args + ) + + user_bookmark_id = Column("user_bookmark_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + position = Column("bookmark_position", Integer(), nullable=False) + title = Column("bookmark_title", String(255), nullable=True, unique=None, default=None) + redirect_url = Column("bookmark_redirect_url", String(10240), nullable=True, unique=None, default=None) + created_on = Column("created_on", DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + + bookmark_repo_id = Column("bookmark_repo_id", Integer(), ForeignKey("repositories.repo_id"), nullable=True, unique=None, default=None) + bookmark_repo_group_id = Column("bookmark_repo_group_id", Integer(), ForeignKey("groups.group_id"), nullable=True, unique=None, default=None) + + user = relationship("User") + + repository = relationship("Repository") + repository_group = relationship("RepoGroup") + + @classmethod + def get_by_position_for_user(cls, position, user_id): + return cls.query() \ + .filter(UserBookmark.user_id == user_id) \ + .filter(UserBookmark.position == position).scalar() + + @classmethod + def get_bookmarks_for_user(cls, user_id, cache=True): + bookmarks = cls.query() \ + .filter(UserBookmark.user_id == user_id) \ + .options(joinedload(UserBookmark.repository)) \ + .options(joinedload(UserBookmark.repository_group)) \ + .order_by(UserBookmark.position.asc()) + + if cache: + bookmarks = bookmarks.options( + FromCache("sql_cache_short", "get_user_{}_bookmarks".format(user_id)) + ) + + return bookmarks.all() + + def __unicode__(self): + return u'' % (self.position, self.redirect_url) + + +class FileStore(Base, BaseModel): + __tablename__ = 'file_store' + __table_args__ = ( + base_table_args + ) + + file_store_id = Column('file_store_id', Integer(), primary_key=True) + file_uid = Column('file_uid', String(1024), nullable=False) + file_display_name = Column('file_display_name', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), nullable=True) + file_description = Column('file_description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True) + file_org_name = Column('file_org_name', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=False) + + # sha256 hash + file_hash = Column('file_hash', String(512), nullable=False) + file_size = Column('file_size', BigInteger(), nullable=False) + + created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True) + accessed_count = Column('accessed_count', Integer(), default=0) + + enabled = Column('enabled', Boolean(), nullable=False, default=True) + + # if repo/repo_group reference is set, check for permissions + check_acl = Column('check_acl', Boolean(), nullable=False, default=True) + + # hidden defines an attachment that should be hidden from showing in artifact listing + hidden = Column('hidden', Boolean(), nullable=False, default=False) + + user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False) + upload_user = relationship('User', lazy='joined', primaryjoin='User.user_id==FileStore.user_id') + + file_metadata = relationship('FileStoreMetadata', lazy='joined') + + # scope limited to user, which requester have access to + scope_user_id = Column( + 'scope_user_id', Integer(), ForeignKey('users.user_id'), + nullable=True, unique=None, default=None) + user = relationship('User', lazy='joined', primaryjoin='User.user_id==FileStore.scope_user_id') + + # scope limited to user group, which requester have access to + scope_user_group_id = Column( + 'scope_user_group_id', Integer(), ForeignKey('users_groups.users_group_id'), + nullable=True, unique=None, default=None) + user_group = relationship('UserGroup', lazy='joined') + + # scope limited to repo, which requester have access to + scope_repo_id = Column( + 'scope_repo_id', Integer(), ForeignKey('repositories.repo_id'), + nullable=True, unique=None, default=None) + repo = relationship('Repository', lazy='joined') + + # scope limited to repo group, which requester have access to + scope_repo_group_id = Column( + 'scope_repo_group_id', Integer(), ForeignKey('groups.group_id'), + nullable=True, unique=None, default=None) + repo_group = relationship('RepoGroup', lazy='joined') + + @classmethod + def get_by_store_uid(cls, file_store_uid): + return FileStore.query().filter(FileStore.file_uid == file_store_uid).scalar() + + @classmethod + def create(cls, file_uid, filename, file_hash, file_size, file_display_name='', + file_description='', enabled=True, hidden=False, check_acl=True, + user_id=None, scope_user_id=None, scope_repo_id=None, scope_repo_group_id=None): + + store_entry = FileStore() + store_entry.file_uid = file_uid + store_entry.file_display_name = file_display_name + store_entry.file_org_name = filename + store_entry.file_size = file_size + store_entry.file_hash = file_hash + store_entry.file_description = file_description + + store_entry.check_acl = check_acl + store_entry.enabled = enabled + store_entry.hidden = hidden + + store_entry.user_id = user_id + store_entry.scope_user_id = scope_user_id + store_entry.scope_repo_id = scope_repo_id + store_entry.scope_repo_group_id = scope_repo_group_id + + return store_entry + + @classmethod + def store_metadata(cls, file_store_id, args, commit=True): + file_store = FileStore.get(file_store_id) + if file_store is None: + return + + for section, key, value, value_type in args: + has_key = FileStoreMetadata().query() \ + .filter(FileStoreMetadata.file_store_id == file_store.file_store_id) \ + .filter(FileStoreMetadata.file_store_meta_section == section) \ + .filter(FileStoreMetadata.file_store_meta_key == key) \ + .scalar() + if has_key: + msg = 'key `{}` already defined under section `{}` for this file.'\ + .format(key, section) + raise ArtifactMetadataDuplicate(msg, err_section=section, err_key=key) + + # NOTE(marcink): raises ArtifactMetadataBadValueType + FileStoreMetadata.valid_value_type(value_type) + + meta_entry = FileStoreMetadata() + meta_entry.file_store = file_store + meta_entry.file_store_meta_section = section + meta_entry.file_store_meta_key = key + meta_entry.file_store_meta_value_type = value_type + meta_entry.file_store_meta_value = value + + Session().add(meta_entry) + + try: + if commit: + Session().commit() + except IntegrityError: + Session().rollback() + raise ArtifactMetadataDuplicate('Duplicate section/key found for this file.') + + @classmethod + def bump_access_counter(cls, file_uid, commit=True): + FileStore().query()\ + .filter(FileStore.file_uid == file_uid)\ + .update({FileStore.accessed_count: (FileStore.accessed_count + 1), + FileStore.accessed_on: datetime.datetime.now()}) + if commit: + Session().commit() + + def __json__(self): + data = { + 'filename': self.file_display_name, + 'filename_org': self.file_org_name, + 'file_uid': self.file_uid, + 'description': self.file_description, + 'hidden': self.hidden, + 'size': self.file_size, + 'created_on': self.created_on, + 'uploaded_by': self.upload_user.get_api_data(details='basic'), + 'downloaded_times': self.accessed_count, + 'sha256': self.file_hash, + 'metadata': self.file_metadata, + } + + return data + + def __repr__(self): + return ''.format(self.file_store_id) + + +class FileStoreMetadata(Base, BaseModel): + __tablename__ = 'file_store_metadata' + __table_args__ = ( + UniqueConstraint('file_store_id', 'file_store_meta_section_hash', 'file_store_meta_key_hash'), + Index('file_store_meta_section_idx', 'file_store_meta_section', mysql_length=255), + Index('file_store_meta_key_idx', 'file_store_meta_key', mysql_length=255), + base_table_args + ) + SETTINGS_TYPES = { + 'str': safe_str, + 'int': safe_int, + 'unicode': safe_unicode, + 'bool': str2bool, + 'list': functools.partial(aslist, sep=',') + } + + file_store_meta_id = Column( + "file_store_meta_id", Integer(), nullable=False, unique=True, default=None, + primary_key=True) + _file_store_meta_section = Column( + "file_store_meta_section", UnicodeText().with_variant(UnicodeText(1024), 'mysql'), + nullable=True, unique=None, default=None) + _file_store_meta_section_hash = Column( + "file_store_meta_section_hash", String(255), + nullable=True, unique=None, default=None) + _file_store_meta_key = Column( + "file_store_meta_key", UnicodeText().with_variant(UnicodeText(1024), 'mysql'), + nullable=True, unique=None, default=None) + _file_store_meta_key_hash = Column( + "file_store_meta_key_hash", String(255), nullable=True, unique=None, default=None) + _file_store_meta_value = Column( + "file_store_meta_value", UnicodeText().with_variant(UnicodeText(20480), 'mysql'), + nullable=True, unique=None, default=None) + _file_store_meta_value_type = Column( + "file_store_meta_value_type", String(255), nullable=True, unique=None, + default='unicode') + + file_store_id = Column( + 'file_store_id', Integer(), ForeignKey('file_store.file_store_id'), + nullable=True, unique=None, default=None) + + file_store = relationship('FileStore', lazy='joined') + + @classmethod + def valid_value_type(cls, value): + if value.split('.')[0] not in cls.SETTINGS_TYPES: + raise ArtifactMetadataBadValueType( + 'value_type must be one of %s got %s' % (cls.SETTINGS_TYPES.keys(), value)) + + @hybrid_property + def file_store_meta_section(self): + return self._file_store_meta_section + + @file_store_meta_section.setter + def file_store_meta_section(self, value): + self._file_store_meta_section = value + self._file_store_meta_section_hash = _hash_key(value) + + @hybrid_property + def file_store_meta_key(self): + return self._file_store_meta_key + + @file_store_meta_key.setter + def file_store_meta_key(self, value): + self._file_store_meta_key = value + self._file_store_meta_key_hash = _hash_key(value) + + @hybrid_property + def file_store_meta_value(self): + val = self._file_store_meta_value + + if self._file_store_meta_value_type: + # e.g unicode.encrypted == unicode + _type = self._file_store_meta_value_type.split('.')[0] + # decode the encrypted value if it's encrypted field type + if '.encrypted' in self._file_store_meta_value_type: + cipher = EncryptedTextValue() + val = safe_unicode(cipher.process_result_value(val, None)) + # do final type conversion + converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode'] + val = converter(val) + + return val + + @file_store_meta_value.setter + def file_store_meta_value(self, val): + val = safe_unicode(val) + # encode the encrypted value + if '.encrypted' in self.file_store_meta_value_type: + cipher = EncryptedTextValue() + val = safe_unicode(cipher.process_bind_param(val, None)) + self._file_store_meta_value = val + + @hybrid_property + def file_store_meta_value_type(self): + return self._file_store_meta_value_type + + @file_store_meta_value_type.setter + def file_store_meta_value_type(self, val): + # e.g unicode.encrypted + self.valid_value_type(val) + self._file_store_meta_value_type = val + + def __json__(self): + data = { + 'artifact': self.file_store.file_uid, + 'section': self.file_store_meta_section, + 'key': self.file_store_meta_key, + 'value': self.file_store_meta_value, + } + + return data + + def __repr__(self): + return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.file_store_meta_section, + self.file_store_meta_key, self.file_store_meta_value) + + +class DbMigrateVersion(Base, BaseModel): + __tablename__ = 'db_migrate_version' + __table_args__ = ( + base_table_args, + ) + + repository_id = Column('repository_id', String(250), primary_key=True) + repository_path = Column('repository_path', Text) + version = Column('version', Integer) + + @classmethod + def set_version(cls, version): + """ + Helper for forcing a different version, usually for debugging purposes via ishell. + """ + ver = DbMigrateVersion.query().first() + ver.version = version + Session().commit() + + +class DbSession(Base, BaseModel): + __tablename__ = 'db_session' + __table_args__ = ( + base_table_args, + ) + + def __repr__(self): + return ''.format(self.id) + + id = Column('id', Integer()) + namespace = Column('namespace', String(255), primary_key=True) + accessed = Column('accessed', DateTime, nullable=False) + created = Column('created', DateTime, nullable=False) + data = Column('data', PickleType, nullable=False) diff --git a/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py b/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py --- a/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py --- a/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py --- a/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/utils.py b/rhodecode/lib/dbmigrate/utils.py --- a/rhodecode/lib/dbmigrate/utils.py +++ b/rhodecode/lib/dbmigrate/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/versions/104_version_4_19_0.py b/rhodecode/lib/dbmigrate/versions/104_version_4_19_0.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/dbmigrate/versions/104_version_4_19_0.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +import logging +from sqlalchemy import * + +from alembic.migration import MigrationContext +from alembic.operations import Operations +from sqlalchemy import BigInteger + +from rhodecode.lib.dbmigrate.versions import _reset_base +from rhodecode.model import init_model_encryption + + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db + + init_model_encryption(db) + + context = MigrationContext.configure(migrate_engine.connect()) + op = Operations(context) + + pull_requests = db.PullRequest.__table__ + + with op.batch_alter_table(pull_requests.name) as batch_op: + new_column = Column( + 'last_merge_metadata', + db.JsonType(dialect_map=dict(mysql=UnicodeText(16384)))) + batch_op.add_column(new_column) + + pull_request_version = db.PullRequestVersion.__table__ + with op.batch_alter_table(pull_request_version.name) as batch_op: + new_column = Column( + 'last_merge_metadata', + db.JsonType(dialect_map=dict(mysql=UnicodeText(16384)))) + batch_op.add_column(new_column) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + +def fixups(models, _SESSION): + pass diff --git a/rhodecode/lib/dbmigrate/versions/105_version_4_19_0.py b/rhodecode/lib/dbmigrate/versions/105_version_4_19_0.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/dbmigrate/versions/105_version_4_19_0.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import logging +from sqlalchemy import * + +from alembic.migration import MigrationContext +from alembic.operations import Operations +from sqlalchemy import BigInteger + +from rhodecode.lib.dbmigrate.versions import _reset_base +from rhodecode.model import init_model_encryption + + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + from rhodecode.lib.dbmigrate.schema import db_4_19_0_0 as db + + init_model_encryption(db) + db.UserNotice().__table__.create() + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + +def fixups(models, _SESSION): + pass diff --git a/rhodecode/lib/dbmigrate/versions/106_version_4_19_0.py b/rhodecode/lib/dbmigrate/versions/106_version_4_19_0.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/dbmigrate/versions/106_version_4_19_0.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import logging +from sqlalchemy import * + +from alembic.migration import MigrationContext +from alembic.operations import Operations +from sqlalchemy import BigInteger + +from rhodecode.lib.dbmigrate.versions import _reset_base +from rhodecode.model import init_model_encryption + + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db + + init_model_encryption(db) + + context = MigrationContext.configure(migrate_engine.connect()) + op = Operations(context) + + comments = db.ChangesetComment.__table__ + + with op.batch_alter_table(comments.name) as batch_op: + new_column = Column('immutable_state', Unicode(128), nullable=True) + batch_op.add_column(new_column) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + +def fixups(models, _SESSION): + pass diff --git a/rhodecode/lib/dbmigrate/versions/107_version_4_19_0.py b/rhodecode/lib/dbmigrate/versions/107_version_4_19_0.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/dbmigrate/versions/107_version_4_19_0.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +import logging +from sqlalchemy import * + +from alembic.migration import MigrationContext +from alembic.operations import Operations +from sqlalchemy import BigInteger + +from rhodecode.lib.dbmigrate.versions import _reset_base +from rhodecode.model import init_model_encryption + + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + from rhodecode.lib.dbmigrate.schema import db_4_19_0_0 as db + + init_model_encryption(db) + + context = MigrationContext.configure(migrate_engine.connect()) + op = Operations(context) + + pull_requests = db.PullRequest.__table__ + with op.batch_alter_table(pull_requests.name) as batch_op: + new_column = Column('common_ancestor_id', Unicode(255), nullable=True) + batch_op.add_column(new_column) + + pull_request_version = db.PullRequestVersion.__table__ + with op.batch_alter_table(pull_request_version.name) as batch_op: + new_column = Column('common_ancestor_id', Unicode(255), nullable=True) + batch_op.add_column(new_column) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + +def fixups(models, _SESSION): + pass diff --git a/rhodecode/lib/dbmigrate/versions/__init__.py b/rhodecode/lib/dbmigrate/versions/__init__.py --- a/rhodecode/lib/dbmigrate/versions/__init__.py +++ b/rhodecode/lib/dbmigrate/versions/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -444,7 +444,8 @@ class DiffProcessor(object): return diff_container(sorted(_files, key=sorter)) def _check_large_diff(self): - log.debug('Diff exceeds current diff_limit of %s', self.diff_limit) + if self.diff_limit: + log.debug('Checking if diff exceeds current diff_limit of %s', self.diff_limit) if not self.show_full_diff and (self.cur_diff_size > self.diff_limit): raise DiffLimitExceeded('Diff Limit `%s` Exceeded', self.diff_limit) diff --git a/rhodecode/lib/encrypt.py b/rhodecode/lib/encrypt.py --- a/rhodecode/lib/encrypt.py +++ b/rhodecode/lib/encrypt.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/exc_tracking.py b/rhodecode/lib/exc_tracking.py --- a/rhodecode/lib/exc_tracking.py +++ b/rhodecode/lib/exc_tracking.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -27,7 +27,6 @@ import traceback import tempfile import glob - log = logging.getLogger(__name__) # NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking @@ -35,7 +34,7 @@ global_prefix = 'rhodecode' exc_store_dir_name = 'rc_exception_store_v1' -def exc_serialize(exc_id, tb, exc_type): +def exc_serialize(exc_id, tb, exc_type, extra_data=None): data = { 'version': 'v1', @@ -45,6 +44,8 @@ def exc_serialize(exc_id, tb, exc_type): 'exc_message': tb, 'exc_type': exc_type, } + if extra_data: + data.update(extra_data) return msgpack.packb(data), data @@ -77,13 +78,24 @@ def get_exc_store(): return _exc_store_path -def _store_exception(exc_id, exc_type_name, exc_traceback, prefix): +def _store_exception(exc_id, exc_type_name, exc_traceback, prefix, send_email=None): """ Low level function to store exception in the exception tracker """ + from pyramid.threadlocal import get_current_request + import rhodecode as app + request = get_current_request() + extra_data = {} + # NOTE(marcink): store request information into exc_data + if request: + extra_data['client_address'] = getattr(request, 'client_addr', '') + extra_data['user_agent'] = getattr(request, 'user_agent', '') + extra_data['method'] = getattr(request, 'method', '') + extra_data['url'] = getattr(request, 'url', '') exc_store_path = get_exc_store() - exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name) + exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name, extra_data=extra_data) + exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp']) if not os.path.isdir(exc_store_path): os.makedirs(exc_store_path) @@ -92,6 +104,52 @@ def _store_exception(exc_id, exc_type_na f.write(exc_data) log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path) + if send_email is None: + # NOTE(marcink): read app config unless we specify explicitly + send_email = app.CONFIG.get('exception_tracker.send_email', False) + + mail_server = app.CONFIG.get('smtp_server') or None + send_email = send_email and mail_server + if send_email: + try: + send_exc_email(request, exc_id, exc_type_name) + except Exception: + log.exception('Failed to send exception email') + pass + + +def send_exc_email(request, exc_id, exc_type_name): + import rhodecode as app + from rhodecode.apps._base import TemplateArgs + from rhodecode.lib.utils2 import aslist + from rhodecode.lib.celerylib import run_task, tasks + from rhodecode.lib.base import attach_context_attributes + from rhodecode.model.notification import EmailNotificationModel + + recipients = aslist(app.CONFIG.get('exception_tracker.send_email_recipients', '')) + log.debug('Sending Email exception to: `%s`', recipients or 'all super admins') + + # NOTE(marcink): needed for email template rendering + user_id = None + if request: + user_id = request.user.user_id + attach_context_attributes(TemplateArgs(), request, user_id=user_id, is_api=True) + + email_kwargs = { + 'email_prefix': app.CONFIG.get('exception_tracker.email_prefix', '') or '[RHODECODE ERROR]', + 'exc_url': request.route_url('admin_settings_exception_tracker_show', exception_id=exc_id), + 'exc_id': exc_id, + 'exc_type_name': exc_type_name, + 'exc_traceback': read_exception(exc_id, prefix=None), + } + + (subject, headers, email_body, + email_body_plaintext) = EmailNotificationModel().render_email( + EmailNotificationModel.TYPE_EMAIL_EXCEPTION, **email_kwargs) + + run_task(tasks.send_email, recipients, subject, + email_body_plaintext, email_body) + def _prepare_exception(exc_info): exc_type, exc_value, exc_traceback = exc_info diff --git a/rhodecode/lib/exceptions.py b/rhodecode/lib/exceptions.py --- a/rhodecode/lib/exceptions.py +++ b/rhodecode/lib/exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -58,6 +58,10 @@ class UserOwnsUserGroupsException(Except pass +class UserOwnsPullRequestsException(Exception): + pass + + class UserOwnsArtifactsException(Exception): pass diff --git a/rhodecode/lib/ext_json_renderer.py b/rhodecode/lib/ext_json_renderer.py --- a/rhodecode/lib/ext_json_renderer.py +++ b/rhodecode/lib/ext_json_renderer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/feedgenerator/__init__.py b/rhodecode/lib/feedgenerator/__init__.py --- a/rhodecode/lib/feedgenerator/__init__.py +++ b/rhodecode/lib/feedgenerator/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/feedgenerator/feedgenerator.py b/rhodecode/lib/feedgenerator/feedgenerator.py --- a/rhodecode/lib/feedgenerator/feedgenerator.py +++ b/rhodecode/lib/feedgenerator/feedgenerator.py @@ -34,6 +34,8 @@ from __future__ import unicode_literals import datetime from StringIO import StringIO + +import pytz from six.moves.urllib import parse as urlparse from rhodecode.lib.feedgenerator import datetime_safe @@ -227,13 +229,13 @@ class SyndicationFeed(object): latest_date = item_date # datetime.now(tz=utc) is slower, as documented in django.utils.timezone.now - return latest_date or datetime.datetime.utcnow().replace(tzinfo=utc) + return latest_date or datetime.datetime.utcnow().replace(tzinfo=pytz.utc) class Enclosure(object): - "Represents an RSS enclosure" + """Represents an RSS enclosure""" def __init__(self, url, length, mime_type): - "All args are expected to be Python Unicode objects" + """All args are expected to be Python Unicode objects""" self.length, self.mime_type = length, mime_type self.url = iri_to_uri(url) diff --git a/rhodecode/lib/graphmod.py b/rhodecode/lib/graphmod.py --- a/rhodecode/lib/graphmod.py +++ b/rhodecode/lib/graphmod.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -253,7 +253,7 @@ class _ToolTip(object): tooltip = _ToolTip() -files_icon = u'' +files_icon = u'' def files_breadcrumbs(repo_name, commit_id, file_path, at_ref=None, limit_items=False, linkify_last_item=False): @@ -1617,18 +1617,19 @@ def render_binary(repo_name, file_obj): Choose how to render a binary file """ + # unicode filename = file_obj.name # images - for ext in ['*.png', '*.jpg', '*.ico', '*.gif']: + for ext in ['*.png', '*.jpeg', '*.jpg', '*.ico', '*.gif']: if fnmatch.fnmatch(filename, pat=ext): - alt = escape(filename) src = route_path( 'repo_file_raw', repo_name=repo_name, commit_id=file_obj.commit.raw_id, f_path=file_obj.path) + return literal( - '{}'.format(alt, src)) + 'rendered-image'.format(src)) def renderer_from_filename(filename, exclude=None): diff --git a/rhodecode/lib/hooks_base.py b/rhodecode/lib/hooks_base.py --- a/rhodecode/lib/hooks_base.py +++ b/rhodecode/lib/hooks_base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -24,7 +24,6 @@ Set of hooks run by RhodeCode Enterprise """ import os -import collections import logging import rhodecode @@ -349,8 +348,8 @@ class ExtensionCallback(object): try: kwargs_to_pass[key] = kwargs[key] except KeyError: - log.error('Failed to fetch %s key. Expected keys: %s', - key, self._kwargs_keys) + log.error('Failed to fetch %s key from given kwargs. ' + 'Expected keys: %s', key, self._kwargs_keys) raise # backward compat for removed api_key for old hooks. This was it works @@ -437,6 +436,15 @@ log_review_pull_request = ExtensionCallb 'mergeable', 'source', 'target', 'author', 'reviewers')) +log_comment_pull_request = ExtensionCallback( + hook_name='COMMENT_PULL_REQUEST', + kwargs_keys=( + 'server_url', 'config', 'scm', 'username', 'ip', 'action', + 'repository', 'pull_request_id', 'url', 'title', 'description', + 'status', 'comment', 'created_on', 'updated_on', 'commit_ids', 'review_status', + 'mergeable', 'source', 'target', 'author', 'reviewers')) + + log_update_pull_request = ExtensionCallback( hook_name='UPDATE_PULL_REQUEST', kwargs_keys=( @@ -484,6 +492,15 @@ log_delete_repository = ExtensionCallbac 'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on')) +log_comment_commit_repository = ExtensionCallback( + hook_name='COMMENT_COMMIT_REPO_HOOK', + kwargs_keys=( + 'repo_name', 'repo_type', 'description', 'private', 'created_on', + 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics', + 'clone_uri', 'fork_id', 'group_id', + 'repository', 'created_by', 'comment', 'commit')) + + log_create_repository_group = ExtensionCallback( hook_name='CREATE_REPO_GROUP_HOOK', kwargs_keys=( diff --git a/rhodecode/lib/hooks_daemon.py b/rhodecode/lib/hooks_daemon.py --- a/rhodecode/lib/hooks_daemon.py +++ b/rhodecode/lib/hooks_daemon.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hooks_utils.py b/rhodecode/lib/hooks_utils.py --- a/rhodecode/lib/hooks_utils.py +++ b/rhodecode/lib/hooks_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -26,144 +26,190 @@ from rhodecode.lib import hooks_base from rhodecode.lib import utils2 -def _get_rc_scm_extras(username, repo_name, repo_alias, action): - # TODO: johbo: Replace by vcs_operation_context and remove fully +def _supports_repo_type(repo_type): + if repo_type in ('hg', 'git'): + return True + return False + + +def _get_vcs_operation_context(username, repo_name, repo_type, action): + # NOTE(dan): import loop from rhodecode.lib.base import vcs_operation_context + check_locking = action in ('pull', 'push') request = get_current_request() - # default - dummy_environ = webob.Request.blank('').environ try: - environ = request.environ or dummy_environ + environ = request.environ except TypeError: # we might use this outside of request context - environ = dummy_environ + environ = {} - extras = vcs_operation_context( - environ, repo_name, username, action, repo_alias, check_locking) + if not environ: + environ = webob.Request.blank('').environ + + extras = vcs_operation_context(environ, repo_name, username, action, repo_type, check_locking) return utils2.AttributeDict(extras) -def trigger_post_push_hook( - username, action, hook_type, repo_name, repo_alias, commit_ids): +def trigger_post_push_hook(username, action, hook_type, repo_name, repo_type, commit_ids): """ Triggers push action hooks :param username: username who pushes :param action: push/push_local/push_remote + :param hook_type: type of hook executed :param repo_name: name of repo - :param repo_alias: the type of SCM repo + :param repo_type: the type of SCM repo :param commit_ids: list of commit ids that we pushed """ - extras = _get_rc_scm_extras(username, repo_name, repo_alias, action) + extras = _get_vcs_operation_context(username, repo_name, repo_type, action) extras.commit_ids = commit_ids extras.hook_type = hook_type hooks_base.post_push(extras) -def trigger_log_create_pull_request_hook(username, repo_name, repo_alias, - pull_request, data=None): +def trigger_comment_commit_hooks(username, repo_name, repo_type, repo, data=None): + """ + Triggers when a comment is made on a commit + + :param username: username who creates the comment + :param repo_name: name of target repo + :param repo_type: the type of SCM target repo + :param repo: the repo object we trigger the event for + :param data: extra data for specific events e.g {'comment': comment_obj, 'commit': commit_obj} + """ + if not _supports_repo_type(repo_type): + return + + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'comment_commit') + + comment = data['comment'] + commit = data['commit'] + + events.trigger(events.RepoCommitCommentEvent(repo, commit, comment)) + extras.update(repo.get_dict()) + + extras.commit = commit.serialize() + extras.comment = comment.get_api_data() + extras.created_by = username + hooks_base.log_comment_commit_repository(**extras) + + +def trigger_create_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers create pull request action hooks :param username: username who creates the pull request :param repo_name: name of target repo - :param repo_alias: the type of SCM target repo + :param repo_type: the type of SCM target repo :param pull_request: the pull request that was created :param data: extra data for specific events e.g {'comment': comment_obj} """ - if repo_alias not in ('hg', 'git'): + if not _supports_repo_type(repo_type): return - extras = _get_rc_scm_extras(username, repo_name, repo_alias, - 'create_pull_request') + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'create_pull_request') events.trigger(events.PullRequestCreateEvent(pull_request)) extras.update(pull_request.get_api_data(with_merge_state=False)) hooks_base.log_create_pull_request(**extras) -def trigger_log_merge_pull_request_hook(username, repo_name, repo_alias, - pull_request, data=None): +def trigger_merge_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers merge pull request action hooks :param username: username who creates the pull request :param repo_name: name of target repo - :param repo_alias: the type of SCM target repo + :param repo_type: the type of SCM target repo :param pull_request: the pull request that was merged :param data: extra data for specific events e.g {'comment': comment_obj} """ - if repo_alias not in ('hg', 'git'): + if not _supports_repo_type(repo_type): return - extras = _get_rc_scm_extras(username, repo_name, repo_alias, - 'merge_pull_request') + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'merge_pull_request') events.trigger(events.PullRequestMergeEvent(pull_request)) extras.update(pull_request.get_api_data()) hooks_base.log_merge_pull_request(**extras) -def trigger_log_close_pull_request_hook(username, repo_name, repo_alias, - pull_request, data=None): +def trigger_close_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers close pull request action hooks :param username: username who creates the pull request :param repo_name: name of target repo - :param repo_alias: the type of SCM target repo + :param repo_type: the type of SCM target repo :param pull_request: the pull request that was closed :param data: extra data for specific events e.g {'comment': comment_obj} """ - if repo_alias not in ('hg', 'git'): + if not _supports_repo_type(repo_type): return - extras = _get_rc_scm_extras(username, repo_name, repo_alias, - 'close_pull_request') + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'close_pull_request') events.trigger(events.PullRequestCloseEvent(pull_request)) extras.update(pull_request.get_api_data()) hooks_base.log_close_pull_request(**extras) -def trigger_log_review_pull_request_hook(username, repo_name, repo_alias, - pull_request, data=None): +def trigger_review_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers review status change pull request action hooks :param username: username who creates the pull request :param repo_name: name of target repo - :param repo_alias: the type of SCM target repo + :param repo_type: the type of SCM target repo :param pull_request: the pull request that review status changed :param data: extra data for specific events e.g {'comment': comment_obj} """ - if repo_alias not in ('hg', 'git'): + if not _supports_repo_type(repo_type): return - extras = _get_rc_scm_extras(username, repo_name, repo_alias, - 'review_pull_request') + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'review_pull_request') status = data.get('status') events.trigger(events.PullRequestReviewEvent(pull_request, status)) extras.update(pull_request.get_api_data()) hooks_base.log_review_pull_request(**extras) -def trigger_log_update_pull_request_hook(username, repo_name, repo_alias, - pull_request, data=None): +def trigger_comment_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): + """ + Triggers when a comment is made on a pull request + + :param username: username who creates the pull request + :param repo_name: name of target repo + :param repo_type: the type of SCM target repo + :param pull_request: the pull request that comment was made on + :param data: extra data for specific events e.g {'comment': comment_obj} + """ + if not _supports_repo_type(repo_type): + return + + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'comment_pull_request') + + comment = data['comment'] + events.trigger(events.PullRequestCommentEvent(pull_request, comment)) + extras.update(pull_request.get_api_data()) + extras.comment = comment.get_api_data() + hooks_base.log_comment_pull_request(**extras) + + +def trigger_update_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers update pull request action hooks :param username: username who creates the pull request :param repo_name: name of target repo - :param repo_alias: the type of SCM target repo + :param repo_type: the type of SCM target repo :param pull_request: the pull request that was updated :param data: extra data for specific events e.g {'comment': comment_obj} """ - if repo_alias not in ('hg', 'git'): + if not _supports_repo_type(repo_type): return - extras = _get_rc_scm_extras(username, repo_name, repo_alias, - 'update_pull_request') + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'update_pull_request') events.trigger(events.PullRequestUpdateEvent(pull_request)) extras.update(pull_request.get_api_data()) hooks_base.log_update_pull_request(**extras) diff --git a/rhodecode/lib/index/__init__.py b/rhodecode/lib/index/__init__.py --- a/rhodecode/lib/index/__init__.py +++ b/rhodecode/lib/index/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/search_utils.py b/rhodecode/lib/index/search_utils.py --- a/rhodecode/lib/index/search_utils.py +++ b/rhodecode/lib/index/search_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/whoosh.py b/rhodecode/lib/index/whoosh.py --- a/rhodecode/lib/index/whoosh.py +++ b/rhodecode/lib/index/whoosh.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/whoosh_fallback_schema.py b/rhodecode/lib/index/whoosh_fallback_schema.py --- a/rhodecode/lib/index/whoosh_fallback_schema.py +++ b/rhodecode/lib/index/whoosh_fallback_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/jsonalchemy.py b/rhodecode/lib/jsonalchemy.py --- a/rhodecode/lib/jsonalchemy.py +++ b/rhodecode/lib/jsonalchemy.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/logging_formatter.py b/rhodecode/lib/logging_formatter.py --- a/rhodecode/lib/logging_formatter.py +++ b/rhodecode/lib/logging_formatter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/markdown_ext.py b/rhodecode/lib/markdown_ext.py --- a/rhodecode/lib/markdown_ext.py +++ b/rhodecode/lib/markdown_ext.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/markup_renderer.py b/rhodecode/lib/markup_renderer.py --- a/rhodecode/lib/markup_renderer.py +++ b/rhodecode/lib/markup_renderer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/memory_lru_dict.py b/rhodecode/lib/memory_lru_dict.py --- a/rhodecode/lib/memory_lru_dict.py +++ b/rhodecode/lib/memory_lru_dict.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/__init__.py b/rhodecode/lib/middleware/__init__.py --- a/rhodecode/lib/middleware/__init__.py +++ b/rhodecode/lib/middleware/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/appenlight.py b/rhodecode/lib/middleware/appenlight.py --- a/rhodecode/lib/middleware/appenlight.py +++ b/rhodecode/lib/middleware/appenlight.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/csrf.py b/rhodecode/lib/middleware/csrf.py --- a/rhodecode/lib/middleware/csrf.py +++ b/rhodecode/lib/middleware/csrf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/https_fixup.py b/rhodecode/lib/middleware/https_fixup.py --- a/rhodecode/lib/middleware/https_fixup.py +++ b/rhodecode/lib/middleware/https_fixup.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/request_wrapper.py b/rhodecode/lib/middleware/request_wrapper.py --- a/rhodecode/lib/middleware/request_wrapper.py +++ b/rhodecode/lib/middleware/request_wrapper.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -22,8 +22,9 @@ import time import logging import rhodecode +from rhodecode.lib.auth import AuthUser from rhodecode.lib.base import get_ip_addr, get_access_path, get_user_agent -from rhodecode.lib.utils2 import safe_str +from rhodecode.lib.utils2 import safe_str, get_current_rhodecode_user log = logging.getLogger(__name__) @@ -46,9 +47,11 @@ class RequestWrapperTween(object): total = end - start count = request.request_count() _ver_ = rhodecode.__version__ + default_user_info = AuthUser.repr_user(ip=get_ip_addr(request.environ)) + user_info = get_current_rhodecode_user(request) or default_user_info log.info( - 'Req[%4s] IP: %s %s Request to %s time: %.4fs [%s], RhodeCode %s', - count, get_ip_addr(request.environ), request.environ.get('REQUEST_METHOD'), + 'Req[%4s] %s %s Request to %s time: %.4fs [%s], RhodeCode %s', + count, user_info, request.environ.get('REQUEST_METHOD'), safe_str(get_access_path(request.environ)), total, get_user_agent(request. environ), _ver_ ) diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py --- a/rhodecode/lib/middleware/simplegit.py +++ b/rhodecode/lib/middleware/simplegit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplehg.py b/rhodecode/lib/middleware/simplehg.py --- a/rhodecode/lib/middleware/simplehg.py +++ b/rhodecode/lib/middleware/simplehg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplesvn.py b/rhodecode/lib/middleware/simplesvn.py --- a/rhodecode/lib/middleware/simplesvn.py +++ b/rhodecode/lib/middleware/simplesvn.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplevcs.py b/rhodecode/lib/middleware/simplevcs.py --- a/rhodecode/lib/middleware/simplevcs.py +++ b/rhodecode/lib/middleware/simplevcs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/__init__.py b/rhodecode/lib/middleware/utils/__init__.py --- a/rhodecode/lib/middleware/utils/__init__.py +++ b/rhodecode/lib/middleware/utils/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/scm_app.py b/rhodecode/lib/middleware/utils/scm_app.py --- a/rhodecode/lib/middleware/utils/scm_app.py +++ b/rhodecode/lib/middleware/utils/scm_app.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/scm_app_http.py b/rhodecode/lib/middleware/utils/scm_app_http.py --- a/rhodecode/lib/middleware/utils/scm_app_http.py +++ b/rhodecode/lib/middleware/utils/scm_app_http.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -79,6 +79,7 @@ class VcsHttpProxy(object): self._repo_name = repo_name self._repo_path = repo_path self._config = config + self.rc_extras = {} log.debug( "Creating VcsHttpProxy for repo %s, url %s", repo_name, url) @@ -87,14 +88,23 @@ class VcsHttpProxy(object): config = msgpack.packb(self._config) request = webob.request.Request(environ) request_headers = request.headers + request_headers.update({ # TODO: johbo: Remove this, rely on URL path only 'X-RC-Repo-Name': self._repo_name, 'X-RC-Repo-Path': self._repo_path, 'X-RC-Path-Info': environ['PATH_INFO'], + + 'X-RC-Repo-Store': self.rc_extras.get('repo_store'), + 'X-RC-Server-Config-File': self.rc_extras.get('config'), + + 'X-RC-Auth-User': self.rc_extras.get('username'), + 'X-RC-Auth-User-Id': self.rc_extras.get('user_id'), + 'X-RC-Auth-User-Ip': self.rc_extras.get('ip'), + # TODO: johbo: Avoid encoding and put this into payload? 'X-RC-Repo-Config': base64.b64encode(config), - 'X-RC-Locked-Status-Code': rhodecode.CONFIG.get('lock_ret_code') + 'X-RC-Locked-Status-Code': rhodecode.CONFIG.get('lock_ret_code'), }) method = environ['REQUEST_METHOD'] diff --git a/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py b/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py --- a/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py +++ b/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/vcs.py b/rhodecode/lib/middleware/vcs.py --- a/rhodecode/lib/middleware/vcs.py +++ b/rhodecode/lib/middleware/vcs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pagination.py b/rhodecode/lib/pagination.py --- a/rhodecode/lib/pagination.py +++ b/rhodecode/lib/pagination.py @@ -877,7 +877,7 @@ class _Page(list): # Below is RhodeCode custom code -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/partial_renderer.py b/rhodecode/lib/partial_renderer.py --- a/rhodecode/lib/partial_renderer.py +++ b/rhodecode/lib/partial_renderer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/paster_commands/__init__.py b/rhodecode/lib/paster_commands/__init__.py --- a/rhodecode/lib/paster_commands/__init__.py +++ b/rhodecode/lib/paster_commands/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/paster_commands/deprecated/celeryd.py b/rhodecode/lib/paster_commands/deprecated/celeryd.py --- a/rhodecode/lib/paster_commands/deprecated/celeryd.py +++ b/rhodecode/lib/paster_commands/deprecated/celeryd.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/paster_commands/deprecated/setup_rhodecode.py b/rhodecode/lib/paster_commands/deprecated/setup_rhodecode.py --- a/rhodecode/lib/paster_commands/deprecated/setup_rhodecode.py +++ b/rhodecode/lib/paster_commands/deprecated/setup_rhodecode.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/paster_commands/ishell.py b/rhodecode/lib/paster_commands/ishell.py --- a/rhodecode/lib/paster_commands/ishell.py +++ b/rhodecode/lib/paster_commands/ishell.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/paster_commands/upgrade_db.py b/rhodecode/lib/paster_commands/upgrade_db.py --- a/rhodecode/lib/paster_commands/upgrade_db.py +++ b/rhodecode/lib/paster_commands/upgrade_db.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pidlock.py b/rhodecode/lib/pidlock.py --- a/rhodecode/lib/pidlock.py +++ b/rhodecode/lib/pidlock.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/plugins/__init__.py b/rhodecode/lib/plugins/__init__.py --- a/rhodecode/lib/plugins/__init__.py +++ b/rhodecode/lib/plugins/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/plugins/utils.py b/rhodecode/lib/plugins/utils.py --- a/rhodecode/lib/plugins/utils.py +++ b/rhodecode/lib/plugins/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pyramid_shell/__init__.py b/rhodecode/lib/pyramid_shell/__init__.py --- a/rhodecode/lib/pyramid_shell/__init__.py +++ b/rhodecode/lib/pyramid_shell/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pyramid_utils.py b/rhodecode/lib/pyramid_utils.py --- a/rhodecode/lib/pyramid_utils.py +++ b/rhodecode/lib/pyramid_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/__init__.py b/rhodecode/lib/rc_cache/__init__.py --- a/rhodecode/lib/rc_cache/__init__.py +++ b/rhodecode/lib/rc_cache/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015-2019 RhodeCode GmbH +# Copyright (C) 2015-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -47,7 +47,7 @@ from .utils import ( FreshRegionCache, ActiveRegionCache) -FILE_TREE_CACHE_VER = 'v2' +FILE_TREE_CACHE_VER = 'v3' def configure_dogpile_cache(settings): diff --git a/rhodecode/lib/rc_cache/backends.py b/rhodecode/lib/rc_cache/backends.py --- a/rhodecode/lib/rc_cache/backends.py +++ b/rhodecode/lib/rc_cache/backends.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015-2019 RhodeCode GmbH +# Copyright (C) 2015-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/cache_key_meta.py b/rhodecode/lib/rc_cache/cache_key_meta.py --- a/rhodecode/lib/rc_cache/cache_key_meta.py +++ b/rhodecode/lib/rc_cache/cache_key_meta.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015-2019 RhodeCode GmbH +# Copyright (C) 2015-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/region_meta.py b/rhodecode/lib/rc_cache/region_meta.py --- a/rhodecode/lib/rc_cache/region_meta.py +++ b/rhodecode/lib/rc_cache/region_meta.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015-2019 RhodeCode GmbH +# Copyright (C) 2015-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/utils.py b/rhodecode/lib/rc_cache/utils.py --- a/rhodecode/lib/rc_cache/utils.py +++ b/rhodecode/lib/rc_cache/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015-2019 RhodeCode GmbH +# Copyright (C) 2015-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/add_artifact.py b/rhodecode/lib/rc_commands/add_artifact.py --- a/rhodecode/lib/rc_commands/add_artifact.py +++ b/rhodecode/lib/rc_commands/add_artifact.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/ishell.py b/rhodecode/lib/rc_commands/ishell.py --- a/rhodecode/lib/rc_commands/ishell.py +++ b/rhodecode/lib/rc_commands/ishell.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/setup_rc.py b/rhodecode/lib/rc_commands/setup_rc.py --- a/rhodecode/lib/rc_commands/setup_rc.py +++ b/rhodecode/lib/rc_commands/setup_rc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/upgrade_db.py b/rhodecode/lib/rc_commands/upgrade_db.py --- a/rhodecode/lib/rc_commands/upgrade_db.py +++ b/rhodecode/lib/rc_commands/upgrade_db.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/repo_maintenance.py b/rhodecode/lib/repo_maintenance.py --- a/rhodecode/lib/repo_maintenance.py +++ b/rhodecode/lib/repo_maintenance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/request.py b/rhodecode/lib/request.py --- a/rhodecode/lib/request.py +++ b/rhodecode/lib/request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/request_counter.py b/rhodecode/lib/request_counter.py --- a/rhodecode/lib/request_counter.py +++ b/rhodecode/lib/request_counter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/system_info.py b/rhodecode/lib/system_info.py --- a/rhodecode/lib/system_info.py +++ b/rhodecode/lib/system_info.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -143,8 +143,9 @@ def python_info(): def py_modules(): - mods = dict([(p.project_name, p.version) + mods = dict([(p.project_name, {'version': p.version, 'location': p.location}) for p in pkg_resources.working_set]) + value = sorted(mods.items(), key=lambda k: k[0].lower()) return SysInfoRes(value=value) diff --git a/rhodecode/lib/user_log_filter.py b/rhodecode/lib/user_log_filter.py --- a/rhodecode/lib/user_log_filter.py +++ b/rhodecode/lib/user_log_filter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/user_sessions.py b/rhodecode/lib/user_sessions.py --- a/rhodecode/lib/user_sessions.py +++ b/rhodecode/lib/user_sessions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/utils2.py b/rhodecode/lib/utils2.py --- a/rhodecode/lib/utils2.py +++ b/rhodecode/lib/utils2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -688,14 +688,17 @@ def get_clone_url(request, uri_tmpl, rep return safe_unicode(url) -def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None): +def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None, + maybe_unreachable=False): """ Safe version of get_commit if this commit doesn't exists for a repository it returns a Dummy one instead :param repo: repository instance :param commit_id: commit id as str + :param commit_idx: numeric commit index :param pre_load: optional list of commit attributes to load + :param maybe_unreachable: translate unreachable commits on git repos """ # TODO(skreft): remove these circular imports from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit @@ -706,7 +709,8 @@ def get_commit_safe(repo, commit_id=None try: commit = repo.get_commit( - commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load) + commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load, + maybe_unreachable=maybe_unreachable) except (RepositoryError, LookupError): commit = EmptyCommit() return commit diff --git a/rhodecode/lib/vcs/__init__.py b/rhodecode/lib/vcs/__init__.py --- a/rhodecode/lib/vcs/__init__.py +++ b/rhodecode/lib/vcs/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/__init__.py b/rhodecode/lib/vcs/backends/__init__.py --- a/rhodecode/lib/vcs/backends/__init__.py +++ b/rhodecode/lib/vcs/backends/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py --- a/rhodecode/lib/vcs/backends/base.py +++ b/rhodecode/lib/vcs/backends/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -216,6 +216,7 @@ class MergeResponse(object): Return a human friendly error message for the given merge status code. """ msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason]) + try: return msg.format(**self.metadata) except Exception: @@ -230,6 +231,14 @@ class MergeResponse(object): return data +class TargetRefMissing(ValueError): + pass + + +class SourceRefMissing(ValueError): + pass + + class BaseRepository(object): """ Base Repository for final backends @@ -437,7 +446,8 @@ class BaseRepository(object): self._invalidate_prop_cache('commit_ids') self._is_empty = False - def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None): + def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, + translate_tag=None, maybe_unreachable=False): """ Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx` are both None, most recent commit is returned. @@ -923,6 +933,9 @@ class BaseCommit(object): d.pop('repository', None) return d + def serialize(self): + return self.__json__() + def _get_refs(self): return { 'branches': [self.branch] if self.branch else [], diff --git a/rhodecode/lib/vcs/backends/git/__init__.py b/rhodecode/lib/vcs/backends/git/__init__.py --- a/rhodecode/lib/vcs/backends/git/__init__.py +++ b/rhodecode/lib/vcs/backends/git/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/commit.py b/rhodecode/lib/vcs/backends/git/commit.py --- a/rhodecode/lib/vcs/backends/git/commit.py +++ b/rhodecode/lib/vcs/backends/git/commit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -307,15 +307,16 @@ class GitCommit(base.BaseCommit): filenodes = [] # extracted tree ID gives us our files... + bytes_path = safe_str(path) # libgit operates on bytes for name, stat_, id_, type_ in self._remote.tree_items(tree_id): if type_ == 'link': - url = self._get_submodule_url('/'.join((path, name))) + url = self._get_submodule_url('/'.join((bytes_path, name))) dirnodes.append(SubModuleNode( name, url=url, commit=id_, alias=self.repository.alias)) continue - if path != '': - obj_path = '/'.join((path, name)) + if bytes_path != '': + obj_path = '/'.join((bytes_path, name)) else: obj_path = name if obj_path not in self._stat_modes: diff --git a/rhodecode/lib/vcs/backends/git/diff.py b/rhodecode/lib/vcs/backends/git/diff.py --- a/rhodecode/lib/vcs/backends/git/diff.py +++ b/rhodecode/lib/vcs/backends/git/diff.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/inmemory.py b/rhodecode/lib/vcs/backends/git/inmemory.py --- a/rhodecode/lib/vcs/backends/git/inmemory.py +++ b/rhodecode/lib/vcs/backends/git/inmemory.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/repository.py b/rhodecode/lib/vcs/backends/git/repository.py --- a/rhodecode/lib/vcs/backends/git/repository.py +++ b/rhodecode/lib/vcs/backends/git/repository.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -228,12 +228,13 @@ class GitRepository(BaseRepository): return [] return output.splitlines() - def _lookup_commit(self, commit_id_or_idx, translate_tag=True): + def _lookup_commit(self, commit_id_or_idx, translate_tag=True, maybe_unreachable=False): def is_null(value): return len(value) == commit_id_or_idx.count('0') if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1): return self.commit_ids[-1] + commit_missing_err = "Commit {} does not exist for `{}`".format( *map(safe_str, [commit_id_or_idx, self.name])) @@ -248,7 +249,8 @@ class GitRepository(BaseRepository): elif is_bstr: # Need to call remote to translate id for tagging scenario try: - remote_data = self._remote.get_object(commit_id_or_idx) + remote_data = self._remote.get_object(commit_id_or_idx, + maybe_unreachable=maybe_unreachable) commit_id_or_idx = remote_data["commit_id"] except (CommitDoesNotExistError,): raise CommitDoesNotExistError(commit_missing_err) @@ -410,7 +412,8 @@ class GitRepository(BaseRepository): except Exception: return - def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=True): + def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, + translate_tag=True, maybe_unreachable=False): """ Returns `GitCommit` object representing commit from git repository at the given `commit_id` or head (most recent commit) if None given. @@ -440,7 +443,7 @@ class GitRepository(BaseRepository): commit_id = "tip" if translate_tag: - commit_id = self._lookup_commit(commit_id) + commit_id = self._lookup_commit(commit_id, maybe_unreachable=maybe_unreachable) try: idx = self._commit_ids[commit_id] @@ -577,6 +580,9 @@ class GitRepository(BaseRepository): return len(self.commit_ids) def get_common_ancestor(self, commit_id1, commit_id2, repo2): + log.debug('Calculating common ancestor between %sc1:%s and %sc2:%s', + self, commit_id1, repo2, commit_id2) + if commit_id1 == commit_id2: return commit_id1 @@ -597,6 +603,8 @@ class GitRepository(BaseRepository): ['merge-base', commit_id1, commit_id2]) ancestor_id = re.findall(r'[0-9a-fA-F]{40}', output)[0] + log.debug('Found common ancestor with sha: %s', ancestor_id) + return ancestor_id def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): @@ -662,6 +670,13 @@ class GitRepository(BaseRepository): self._remote.remove_ref(ref_name) self._invalidate_prop_cache('_refs') + def run_gc(self, prune=True): + cmd = ['gc', '--aggressive'] + if prune: + cmd += ['--prune=now'] + _stdout, stderr = self.run_git_command(cmd, fail_on_stderr=False) + return stderr + def _update_server_info(self): """ runs gits update-server-info command in this repo instance @@ -827,8 +842,7 @@ class GitRepository(BaseRepository): if self.is_empty(): # TODO(skreft): do something more robust in this case. - raise RepositoryError( - 'Do not know how to merge into empty repositories yet') + raise RepositoryError('Do not know how to merge into empty repositories yet') unresolved = None # N.B.(skreft): the --no-ff option is used to enforce the creation of a @@ -836,9 +850,11 @@ class GitRepository(BaseRepository): cmd = ['-c', 'user.name="%s"' % safe_str(user_name), '-c', 'user.email=%s' % safe_str(user_email), 'merge', '--no-ff', '-m', safe_str(merge_message)] - cmd.extend(heads) + + merge_cmd = cmd + heads + try: - output = self.run_git_command(cmd, fail_on_stderr=False) + self.run_git_command(merge_cmd, fail_on_stderr=False) except RepositoryError: files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'], fail_on_stderr=False)[0].splitlines() @@ -846,6 +862,7 @@ class GitRepository(BaseRepository): unresolved = ['U {}'.format(f) for f in files] # Cleanup any merge leftovers + self._remote.invalidate_vcs_cache() self.run_git_command(['merge', '--abort'], fail_on_stderr=False) if unresolved: diff --git a/rhodecode/lib/vcs/backends/hg/__init__.py b/rhodecode/lib/vcs/backends/hg/__init__.py --- a/rhodecode/lib/vcs/backends/hg/__init__.py +++ b/rhodecode/lib/vcs/backends/hg/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py --- a/rhodecode/lib/vcs/backends/hg/commit.py +++ b/rhodecode/lib/vcs/backends/hg/commit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/diff.py b/rhodecode/lib/vcs/backends/hg/diff.py --- a/rhodecode/lib/vcs/backends/hg/diff.py +++ b/rhodecode/lib/vcs/backends/hg/diff.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/inmemory.py b/rhodecode/lib/vcs/backends/hg/inmemory.py --- a/rhodecode/lib/vcs/backends/hg/inmemory.py +++ b/rhodecode/lib/vcs/backends/hg/inmemory.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/repository.py b/rhodecode/lib/vcs/backends/hg/repository.py --- a/rhodecode/lib/vcs/backends/hg/repository.py +++ b/rhodecode/lib/vcs/backends/hg/repository.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -297,13 +297,20 @@ class MercurialRepository(BaseRepository return update_cache def get_common_ancestor(self, commit_id1, commit_id2, repo2): + log.debug('Calculating common ancestor between %sc1:%s and %sc2:%s', + self, commit_id1, repo2, commit_id2) + if commit_id1 == commit_id2: return commit_id1 ancestors = self._remote.revs_from_revspec( "ancestor(id(%s), id(%s))", commit_id1, commit_id2, other_path=repo2.path) - return repo2[ancestors[0]].raw_id if ancestors else None + + ancestor_id = repo2[ancestors[0]].raw_id if ancestors else None + + log.debug('Found common ancestor with sha: %s', ancestor_id) + return ancestor_id def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): if commit_id1 == commit_id2: @@ -429,7 +436,8 @@ class MercurialRepository(BaseRepository """ return os.path.join(self.path, '.hg', '.hgrc') - def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None): + def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, + translate_tag=None, maybe_unreachable=False): """ Returns ``MercurialCommit`` object representing repository's commit at the given `commit_id` or `commit_idx`. diff --git a/rhodecode/lib/vcs/backends/svn/__init__.py b/rhodecode/lib/vcs/backends/svn/__init__.py --- a/rhodecode/lib/vcs/backends/svn/__init__.py +++ b/rhodecode/lib/vcs/backends/svn/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/commit.py b/rhodecode/lib/vcs/backends/svn/commit.py --- a/rhodecode/lib/vcs/backends/svn/commit.py +++ b/rhodecode/lib/vcs/backends/svn/commit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -173,11 +173,10 @@ class SubversionCommit(base.BaseCommit): raise CommitError( "Directory does not exist for commit %s at " " '%s'" % (self.raw_id, path)) - path = self._fix_path(path) + path = safe_str(self._fix_path(path)) path_nodes = [] - for name, kind in self._remote.get_nodes( - safe_str(path), revision=self._svn_rev): + for name, kind in self._remote.get_nodes(path, revision=self._svn_rev): node_path = vcspath.join(path, name) if kind == 'dir': node = nodes.DirNode(node_path, commit=self) diff --git a/rhodecode/lib/vcs/backends/svn/diff.py b/rhodecode/lib/vcs/backends/svn/diff.py --- a/rhodecode/lib/vcs/backends/svn/diff.py +++ b/rhodecode/lib/vcs/backends/svn/diff.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/inmemory.py b/rhodecode/lib/vcs/backends/svn/inmemory.py --- a/rhodecode/lib/vcs/backends/svn/inmemory.py +++ b/rhodecode/lib/vcs/backends/svn/inmemory.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/repository.py b/rhodecode/lib/vcs/backends/svn/repository.py --- a/rhodecode/lib/vcs/backends/svn/repository.py +++ b/rhodecode/lib/vcs/backends/svn/repository.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -276,7 +276,8 @@ class SubversionRepository(base.BaseRepo """ return os.path.join(self.path, 'hooks') - def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None): + def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, + translate_tag=None, maybe_unreachable=False): if self.is_empty(): raise EmptyRepositoryError("There are no commits yet") if commit_id is not None: diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/conf/__init__.py b/rhodecode/lib/vcs/conf/__init__.py --- a/rhodecode/lib/vcs/conf/__init__.py +++ b/rhodecode/lib/vcs/conf/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/conf/mtypes.py b/rhodecode/lib/vcs/conf/mtypes.py --- a/rhodecode/lib/vcs/conf/mtypes.py +++ b/rhodecode/lib/vcs/conf/mtypes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/conf/settings.py b/rhodecode/lib/vcs/conf/settings.py --- a/rhodecode/lib/vcs/conf/settings.py +++ b/rhodecode/lib/vcs/conf/settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/connection.py b/rhodecode/lib/vcs/connection.py --- a/rhodecode/lib/vcs/connection.py +++ b/rhodecode/lib/vcs/connection.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/exceptions.py b/rhodecode/lib/vcs/exceptions.py --- a/rhodecode/lib/vcs/exceptions.py +++ b/rhodecode/lib/vcs/exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/geventcurl.py b/rhodecode/lib/vcs/geventcurl.py --- a/rhodecode/lib/vcs/geventcurl.py +++ b/rhodecode/lib/vcs/geventcurl.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/nodes.py b/rhodecode/lib/vcs/nodes.py --- a/rhodecode/lib/vcs/nodes.py +++ b/rhodecode/lib/vcs/nodes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/path.py b/rhodecode/lib/vcs/path.py --- a/rhodecode/lib/vcs/path.py +++ b/rhodecode/lib/vcs/path.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/__init__.py b/rhodecode/lib/vcs/utils/__init__.py --- a/rhodecode/lib/vcs/utils/__init__.py +++ b/rhodecode/lib/vcs/utils/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/helpers.py b/rhodecode/lib/vcs/utils/helpers.py --- a/rhodecode/lib/vcs/utils/helpers.py +++ b/rhodecode/lib/vcs/utils/helpers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2019 RhodeCode GmbH +# Copyright (C) 2014-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/imports.py b/rhodecode/lib/vcs/utils/imports.py --- a/rhodecode/lib/vcs/utils/imports.py +++ b/rhodecode/lib/vcs/utils/imports.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/paths.py b/rhodecode/lib/vcs/utils/paths.py --- a/rhodecode/lib/vcs/utils/paths.py +++ b/rhodecode/lib/vcs/utils/paths.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/view_utils.py b/rhodecode/lib/view_utils.py --- a/rhodecode/lib/view_utils.py +++ b/rhodecode/lib/view_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/__init__.py b/rhodecode/model/__init__.py --- a/rhodecode/model/__init__.py +++ b/rhodecode/model/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/auth_token.py b/rhodecode/model/auth_token.py --- a/rhodecode/model/auth_token.py +++ b/rhodecode/model/auth_token.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/changeset_status.py b/rhodecode/model/changeset_status.py --- a/rhodecode/model/changeset_status.py +++ b/rhodecode/model/changeset_status.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -30,7 +30,7 @@ from pyramid.threadlocal import get_curr from sqlalchemy.sql.expression import null from sqlalchemy.sql.functions import coalesce -from rhodecode.lib import helpers as h, diffs, channelstream +from rhodecode.lib import helpers as h, diffs, channelstream, hooks_utils from rhodecode.lib import audit_logger from rhodecode.lib.utils2 import extract_mentioned_users, safe_str from rhodecode.model import BaseModel @@ -720,6 +720,26 @@ class CommentsModel(BaseModel): settings = settings_model.get_general_settings() return settings.get('rhodecode_use_outdated_comments', False) + def trigger_commit_comment_hook(self, repo, user, action, data=None): + repo = self._get_repo(repo) + target_scm = repo.scm_instance() + if action == 'create': + trigger_hook = hooks_utils.trigger_comment_commit_hooks + elif action == 'edit': + # TODO(dan): when this is supported we trigger edit hook too + return + else: + return + + log.debug('Handling repo %s trigger_commit_comment_hook with action %s: %s', + repo, action, trigger_hook) + trigger_hook( + username=user.username, + repo_name=repo.repo_name, + repo_type=target_scm.alias, + repo=repo, + data=data) + def _parse_comment_line_number(line_no): """ diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -617,6 +617,7 @@ class User(Base, BaseModel): user_gists = relationship('Gist', cascade='all') # user pull requests user_pull_requests = relationship('PullRequest', cascade='all') + # external identities external_identities = relationship( 'ExternalIdentity', @@ -1048,6 +1049,11 @@ class User(Base, BaseModel): Session().refresh(user) return user + @classmethod + def get_default_user_id(cls): + import rhodecode + return rhodecode.CONFIG['default_user_id'] + def _get_default_perms(self, user, suffix=''): from rhodecode.model.permission import PermissionModel return PermissionModel().get_default_perms(user.user_perms, suffix) @@ -2339,9 +2345,10 @@ class Repository(Base, BaseModel): # SCM PROPERTIES #========================================================================== - def get_commit(self, commit_id=None, commit_idx=None, pre_load=None): + def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, maybe_unreachable=False): return get_commit_safe( - self.scm_instance(), commit_id, commit_idx, pre_load=pre_load) + self.scm_instance(), commit_id, commit_idx, pre_load=pre_load, + maybe_unreachable=maybe_unreachable) def get_changeset(self, rev=None, pre_load=None): warnings.warn("Use get_commit", DeprecationWarning) @@ -3710,6 +3717,9 @@ class ChangesetComment(Base, BaseModel): COMMENT_TYPE_TODO = u'todo' COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO] + OP_IMMUTABLE = u'immutable' + OP_CHANGEABLE = u'changeable' + comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) revision = Column('revision', String(40), nullable=True) @@ -3724,6 +3734,7 @@ class ChangesetComment(Base, BaseModel): modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) renderer = Column('renderer', Unicode(64), nullable=True) display_state = Column('display_state', Unicode(128), nullable=True) + immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE) comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) @@ -3766,6 +3777,10 @@ class ChangesetComment(Base, BaseModel): def outdated(self): return self.display_state == self.COMMENT_OUTDATED + @property + def immutable(self): + return self.immutable_state == self.OP_IMMUTABLE + def outdated_at_version(self, version): """ Checks if comment is outdated for given pull request version @@ -3814,7 +3829,9 @@ class ChangesetComment(Base, BaseModel): 'comment_lineno': comment.line_no, 'comment_author': comment.author, 'comment_created_on': comment.created_on, - 'comment_resolved_by': self.resolved + 'comment_resolved_by': self.resolved, + 'comment_commit_id': comment.revision, + 'comment_pull_request_id': comment.pull_request_id, } return data @@ -3973,6 +3990,8 @@ class _PullRequestBase(BaseModel): _revisions = Column( 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql')) + common_ancestor_id = Column('common_ancestor_id', Unicode(255), nullable=True) + @declared_attr def source_repo_id(cls): # TODO: dan: rename column to source_repo_id @@ -4024,6 +4043,10 @@ class _PullRequestBase(BaseModel): _last_merge_target_rev = Column( 'last_merge_other_rev', String(40), nullable=True) _last_merge_status = Column('merge_status', Integer(), nullable=True) + last_merge_metadata = Column( + 'last_merge_metadata', MutationObj.as_mutable( + JsonType(dialect_map=dict(mysql=UnicodeText(16384))))) + merge_rev = Column('merge_rev', String(40), nullable=True) reviewer_data = Column( @@ -4123,10 +4146,11 @@ class _PullRequestBase(BaseModel): pull_request = self if with_merge_state: - merge_status = PullRequestModel().merge_status(pull_request) + merge_response, merge_status, msg = \ + PullRequestModel().merge_status(pull_request) merge_state = { - 'status': merge_status[0], - 'message': safe_unicode(merge_status[1]), + 'status': merge_status, + 'message': safe_unicode(msg), } else: merge_state = {'status': 'not_available', @@ -4281,7 +4305,7 @@ class PullRequest(Base, _PullRequestBase attrs.source_ref_parts = pull_request_obj.source_ref_parts attrs.target_ref_parts = pull_request_obj.target_ref_parts attrs.revisions = pull_request_obj.revisions - + attrs.common_ancestor_id = pull_request_obj.common_ancestor_id attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref attrs.reviewer_data = org_pull_request_obj.reviewer_data attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json @@ -4511,6 +4535,65 @@ class UserNotification(Base, BaseModel): Session().add(self) +class UserNotice(Base, BaseModel): + __tablename__ = 'user_notices' + __table_args__ = ( + base_table_args + ) + + NOTIFICATION_TYPE_MESSAGE = 'message' + NOTIFICATION_TYPE_NOTICE = 'notice' + + NOTIFICATION_LEVEL_INFO = 'info' + NOTIFICATION_LEVEL_WARNING = 'warning' + NOTIFICATION_LEVEL_ERROR = 'error' + + user_notice_id = Column('gist_id', Integer(), primary_key=True) + + notice_subject = Column('notice_subject', Unicode(512), nullable=True) + notice_body = Column('notice_body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True) + + notice_read = Column('notice_read', Boolean, default=False) + + notification_level = Column('notification_level', String(1024), default=NOTIFICATION_LEVEL_INFO) + notification_type = Column('notification_type', String(1024), default=NOTIFICATION_TYPE_NOTICE) + + notice_created_by = Column('notice_created_by', Integer(), ForeignKey('users.user_id'), nullable=True) + notice_created_on = Column('notice_created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) + + user_id = Column('user_id', Integer(), ForeignKey('users.user_id')) + user = relationship('User', lazy="joined", primaryjoin='User.user_id==UserNotice.user_id') + + @classmethod + def create_for_user(cls, user, subject, body, notice_level=NOTIFICATION_LEVEL_INFO, allow_duplicate=False): + + if notice_level not in [cls.NOTIFICATION_LEVEL_ERROR, + cls.NOTIFICATION_LEVEL_WARNING, + cls.NOTIFICATION_LEVEL_INFO]: + return + + from rhodecode.model.user import UserModel + user = UserModel().get_user(user) + + new_notice = UserNotice() + if not allow_duplicate: + existing_msg = UserNotice().query() \ + .filter(UserNotice.user == user) \ + .filter(UserNotice.notice_body == body) \ + .filter(UserNotice.notice_read == false()) \ + .scalar() + if existing_msg: + log.warning('Ignoring duplicate notice for user %s', user) + return + + new_notice.user = user + new_notice.notice_subject = subject + new_notice.notice_body = body + new_notice.notification_level = notice_level + Session().add(new_notice) + Session().commit() + + class Gist(Base, BaseModel): __tablename__ = 'gists' __table_args__ = ( diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/gist.py b/rhodecode/model/gist.py --- a/rhodecode/model/gist.py +++ b/rhodecode/model/gist.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/integration.py b/rhodecode/model/integration.py --- a/rhodecode/model/integration.py +++ b/rhodecode/model/integration.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/meta.py b/rhodecode/model/meta.py --- a/rhodecode/model/meta.py +++ b/rhodecode/model/meta.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -26,6 +26,7 @@ Model for notifications import logging import traceback +import premailer from pyramid.threadlocal import get_current_request from sqlalchemy.sql.expression import false, true @@ -303,6 +304,7 @@ class EmailNotificationModel(BaseModel): TYPE_PASSWORD_RESET = 'password_reset' TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation' TYPE_EMAIL_TEST = 'email_test' + TYPE_EMAIL_EXCEPTION = 'exception' TYPE_TEST = 'test' email_types = { @@ -310,6 +312,8 @@ class EmailNotificationModel(BaseModel): 'rhodecode:templates/email_templates/main.mako', TYPE_TEST: 'rhodecode:templates/email_templates/test.mako', + TYPE_EMAIL_EXCEPTION: + 'rhodecode:templates/email_templates/exception_tracker.mako', TYPE_EMAIL_TEST: 'rhodecode:templates/email_templates/email_test.mako', TYPE_REGISTRATION: @@ -328,6 +332,12 @@ class EmailNotificationModel(BaseModel): 'rhodecode:templates/email_templates/pull_request_update.mako', } + premailer_instance = premailer.Premailer( + cssutils_logging_level=logging.WARNING, + cssutils_logging_handler=logging.getLogger().handlers[0] + if logging.getLogger().handlers else None, + ) + def __init__(self): """ Example usage:: @@ -391,4 +401,11 @@ class EmailNotificationModel(BaseModel): # render WHOLE template body = email_template.render(None, **_kwargs) + try: + # Inline CSS styles and conversion + body = self.premailer_instance.transform(body) + except Exception: + log.exception('Failed to parse body with premailer') + pass + return subject, headers, body, body_plaintext diff --git a/rhodecode/model/permission.py b/rhodecode/model/permission.py --- a/rhodecode/model/permission.py +++ b/rhodecode/model/permission.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -558,7 +558,7 @@ class PermissionModel(BaseModel): def get_users_with_repo_write(self, db_repo): write_plus = ['repository.write', 'repository.admin'] - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() user_write_permissions = collections.OrderedDict() # write+ and DEFAULT user for inheritance diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -35,7 +35,7 @@ import collections from pyramid import compat from pyramid.threadlocal import get_current_request -from rhodecode import events +from rhodecode.lib.vcs.nodes import FileNode from rhodecode.translation import lazy_ugettext from rhodecode.lib import helpers as h, hooks_utils, diffs from rhodecode.lib import audit_logger @@ -43,9 +43,12 @@ from rhodecode.lib.compat import Ordered from rhodecode.lib.hooks_daemon import prepare_callback_daemon from rhodecode.lib.markup_renderer import ( DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer) -from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe +from rhodecode.lib.utils2 import ( + safe_unicode, safe_str, md5_safe, AttributeDict, safe_int, + get_current_rhodecode_user) from rhodecode.lib.vcs.backends.base import ( - Reference, MergeResponse, MergeFailureReason, UpdateFailureReason) + Reference, MergeResponse, MergeFailureReason, UpdateFailureReason, + TargetRefMissing, SourceRefMissing) from rhodecode.lib.vcs.conf import settings as vcs_settings from rhodecode.lib.vcs.exceptions import ( CommitDoesNotExistError, EmptyRepositoryError) @@ -54,7 +57,7 @@ from rhodecode.model.changeset_status im from rhodecode.model.comment import CommentsModel from rhodecode.model.db import ( or_, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus, - PullRequestVersion, ChangesetComment, Repository, RepoReviewRule) + PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User) from rhodecode.model.meta import Session from rhodecode.model.notification import NotificationModel, \ EmailNotificationModel @@ -82,6 +85,126 @@ class UpdateResponse(object): self.target_changed = target_changed +def get_diff_info( + source_repo, source_ref, target_repo, target_ref, get_authors=False, + get_commit_authors=True): + """ + Calculates detailed diff information for usage in preview of creation of a pull-request. + This is also used for default reviewers logic + """ + + source_scm = source_repo.scm_instance() + target_scm = target_repo.scm_instance() + + ancestor_id = target_scm.get_common_ancestor(target_ref, source_ref, source_scm) + if not ancestor_id: + raise ValueError( + 'cannot calculate diff info without a common ancestor. ' + 'Make sure both repositories are related, and have a common forking commit.') + + # case here is that want a simple diff without incoming commits, + # previewing what will be merged based only on commits in the source. + log.debug('Using ancestor %s as source_ref instead of %s', + ancestor_id, source_ref) + + # source of changes now is the common ancestor + source_commit = source_scm.get_commit(commit_id=ancestor_id) + # target commit becomes the source ref as it is the last commit + # for diff generation this logic gives proper diff + target_commit = source_scm.get_commit(commit_id=source_ref) + + vcs_diff = \ + source_scm.get_diff(commit1=source_commit, commit2=target_commit, + ignore_whitespace=False, context=3) + + diff_processor = diffs.DiffProcessor( + vcs_diff, format='newdiff', diff_limit=None, + file_limit=None, show_full_diff=True) + + _parsed = diff_processor.prepare() + + all_files = [] + all_files_changes = [] + changed_lines = {} + stats = [0, 0] + for f in _parsed: + all_files.append(f['filename']) + all_files_changes.append({ + 'filename': f['filename'], + 'stats': f['stats'] + }) + stats[0] += f['stats']['added'] + stats[1] += f['stats']['deleted'] + + changed_lines[f['filename']] = [] + if len(f['chunks']) < 2: + continue + # first line is "context" information + for chunks in f['chunks'][1:]: + for chunk in chunks['lines']: + if chunk['action'] not in ('del', 'mod'): + continue + changed_lines[f['filename']].append(chunk['old_lineno']) + + commit_authors = [] + user_counts = {} + email_counts = {} + author_counts = {} + _commit_cache = {} + + commits = [] + if get_commit_authors: + commits = target_scm.compare( + target_ref, source_ref, source_scm, merge=True, + pre_load=["author"]) + + for commit in commits: + user = User.get_from_cs_author(commit.author) + if user and user not in commit_authors: + commit_authors.append(user) + + # lines + if get_authors: + target_commit = source_repo.get_commit(ancestor_id) + + for fname, lines in changed_lines.items(): + try: + node = target_commit.get_node(fname) + except Exception: + continue + + if not isinstance(node, FileNode): + continue + + for annotation in node.annotate: + line_no, commit_id, get_commit_func, line_text = annotation + if line_no in lines: + if commit_id not in _commit_cache: + _commit_cache[commit_id] = get_commit_func() + commit = _commit_cache[commit_id] + author = commit.author + email = commit.author_email + user = User.get_from_cs_author(author) + if user: + user_counts[user] = user_counts.get(user, 0) + 1 + author_counts[author] = author_counts.get(author, 0) + 1 + email_counts[email] = email_counts.get(email, 0) + 1 + + return { + 'commits': commits, + 'files': all_files_changes, + 'stats': stats, + 'ancestor': ancestor_id, + # original authors of modified files + 'original_authors': { + 'users': user_counts, + 'authors': author_counts, + 'emails': email_counts, + }, + 'commit_authors': commit_authors + } + + class PullRequestModel(BaseModel): cls = PullRequest @@ -160,8 +283,10 @@ class PullRequestModel(BaseModel): if search_q: like_expression = u'%{}%'.format(safe_unicode(search_q)) + q = q.join(User) q = q.filter(or_( cast(PullRequest.pull_request_id, String).ilike(like_expression), + User.username.ilike(like_expression), PullRequest.title.ilike(like_expression), PullRequest.description.ilike(like_expression), )) @@ -355,7 +480,7 @@ class PullRequestModel(BaseModel): PullRequestReviewers.user_id == user_id).all() ] - def _prepare_participating_query(self, user_id=None, statuses=None, + def _prepare_participating_query(self, user_id=None, statuses=None, query='', order_by=None, order_dir='desc'): q = PullRequest.query() if user_id: @@ -372,6 +497,15 @@ class PullRequestModel(BaseModel): if statuses: q = q.filter(PullRequest.status.in_(statuses)) + if query: + like_expression = u'%{}%'.format(safe_unicode(query)) + q = q.join(User) + q = q.filter(or_( + cast(PullRequest.pull_request_id, String).ilike(like_expression), + User.username.ilike(like_expression), + PullRequest.title.ilike(like_expression), + PullRequest.description.ilike(like_expression), + )) if order_by: order_map = { 'name_raw': PullRequest.pull_request_id, @@ -386,19 +520,19 @@ class PullRequestModel(BaseModel): return q - def count_im_participating_in(self, user_id=None, statuses=None): - q = self._prepare_participating_query(user_id, statuses=statuses) + def count_im_participating_in(self, user_id=None, statuses=None, query=''): + q = self._prepare_participating_query(user_id, statuses=statuses, query=query) return q.count() def get_im_participating_in( - self, user_id=None, statuses=None, offset=0, + self, user_id=None, statuses=None, query='', offset=0, length=None, order_by=None, order_dir='desc'): """ Get all Pull requests that i'm participating in, or i have opened """ q = self._prepare_participating_query( - user_id, statuses=statuses, order_by=order_by, + user_id, statuses=statuses, query=query, order_by=order_by, order_dir=order_dir) if length: @@ -442,6 +576,7 @@ class PullRequestModel(BaseModel): def create(self, created_by, source_repo, source_ref, target_repo, target_ref, revisions, reviewers, title, description=None, + common_ancestor_id=None, description_renderer=None, reviewer_data=None, translator=None, auth_user=None): translator = translator or get_current_request().translate @@ -463,6 +598,8 @@ class PullRequestModel(BaseModel): pull_request.author = created_by_user pull_request.reviewer_data = reviewer_data pull_request.pull_request_state = pull_request.STATE_CREATING + pull_request.common_ancestor_id = common_ancestor_id + Session().add(pull_request) Session().flush() @@ -542,8 +679,7 @@ class PullRequestModel(BaseModel): pull_request, auth_user=auth_user, translator=translator) self.notify_reviewers(pull_request, reviewer_ids) - self.trigger_pull_request_hook( - pull_request, created_by_user, 'create') + self.trigger_pull_request_hook(pull_request, created_by_user, 'create') creation_data = pull_request.get_api_data(with_merge_state=False) self._log_audit_action( @@ -556,28 +692,26 @@ class PullRequestModel(BaseModel): pull_request = self.__get_pull_request(pull_request) target_scm = pull_request.target_repo.scm_instance() if action == 'create': - trigger_hook = hooks_utils.trigger_log_create_pull_request_hook + trigger_hook = hooks_utils.trigger_create_pull_request_hook elif action == 'merge': - trigger_hook = hooks_utils.trigger_log_merge_pull_request_hook + trigger_hook = hooks_utils.trigger_merge_pull_request_hook elif action == 'close': - trigger_hook = hooks_utils.trigger_log_close_pull_request_hook + trigger_hook = hooks_utils.trigger_close_pull_request_hook elif action == 'review_status_change': - trigger_hook = hooks_utils.trigger_log_review_pull_request_hook + trigger_hook = hooks_utils.trigger_review_pull_request_hook elif action == 'update': - trigger_hook = hooks_utils.trigger_log_update_pull_request_hook + trigger_hook = hooks_utils.trigger_update_pull_request_hook elif action == 'comment': - # dummy hook ! for comment. We want this function to handle all cases - def trigger_hook(*args, **kwargs): - pass - comment = data['comment'] - events.trigger(events.PullRequestCommentEvent(pull_request, comment)) + trigger_hook = hooks_utils.trigger_comment_pull_request_hook else: return + log.debug('Handling pull_request %s trigger_pull_request_hook with action %s and hook: %s', + pull_request, action, trigger_hook) trigger_hook( username=user.username, repo_name=pull_request.target_repo.repo_name, - repo_alias=target_scm.alias, + repo_type=target_scm.alias, pull_request=pull_request, data=data) @@ -684,6 +818,38 @@ class PullRequestModel(BaseModel): source_ref_type = pull_request.source_ref_parts.type return source_ref_type in self.REF_TYPES + def get_flow_commits(self, pull_request): + + # source repo + source_ref_name = pull_request.source_ref_parts.name + source_ref_type = pull_request.source_ref_parts.type + source_ref_id = pull_request.source_ref_parts.commit_id + source_repo = pull_request.source_repo.scm_instance() + + try: + if source_ref_type in self.REF_TYPES: + source_commit = source_repo.get_commit(source_ref_name) + else: + source_commit = source_repo.get_commit(source_ref_id) + except CommitDoesNotExistError: + raise SourceRefMissing() + + # target repo + target_ref_name = pull_request.target_ref_parts.name + target_ref_type = pull_request.target_ref_parts.type + target_ref_id = pull_request.target_ref_parts.commit_id + target_repo = pull_request.target_repo.scm_instance() + + try: + if target_ref_type in self.REF_TYPES: + target_commit = target_repo.get_commit(target_ref_name) + else: + target_commit = target_repo.get_commit(target_ref_id) + except CommitDoesNotExistError: + raise TargetRefMissing() + + return source_commit, target_commit + def update_commits(self, pull_request, updating_user): """ Get the updated list of commits for the pull request @@ -710,31 +876,22 @@ class PullRequestModel(BaseModel): old=pull_request, new=None, common_ancestor_id=None, commit_changes=None, source_changed=False, target_changed=False) - # source repo - source_repo = pull_request.source_repo.scm_instance() - try: - source_commit = source_repo.get_commit(commit_id=source_ref_name) - except CommitDoesNotExistError: + source_commit, target_commit = self.get_flow_commits(pull_request) + except SourceRefMissing: return UpdateResponse( executed=False, reason=UpdateFailureReason.MISSING_SOURCE_REF, old=pull_request, new=None, common_ancestor_id=None, commit_changes=None, source_changed=False, target_changed=False) - - source_changed = source_ref_id != source_commit.raw_id - - # target repo - target_repo = pull_request.target_repo.scm_instance() - - try: - target_commit = target_repo.get_commit(commit_id=target_ref_name) - except CommitDoesNotExistError: + except TargetRefMissing: return UpdateResponse( executed=False, reason=UpdateFailureReason.MISSING_TARGET_REF, old=pull_request, new=None, common_ancestor_id=None, commit_changes=None, source_changed=False, target_changed=False) + + source_changed = source_ref_id != source_commit.raw_id target_changed = target_ref_id != target_commit.raw_id if not (source_changed or target_changed): @@ -764,17 +921,8 @@ class PullRequestModel(BaseModel): ver.pull_request_version_id if ver else None pull_request_version = pull_request - try: - if target_ref_type in self.REF_TYPES: - target_commit = target_repo.get_commit(target_ref_name) - else: - target_commit = target_repo.get_commit(target_ref_id) - except CommitDoesNotExistError: - return UpdateResponse( - executed=False, - reason=UpdateFailureReason.MISSING_TARGET_REF, - old=pull_request, new=None, common_ancestor_id=None, commit_changes=None, - source_changed=source_changed, target_changed=target_changed) + source_repo = pull_request.source_repo.scm_instance() + target_repo = pull_request.target_repo.scm_instance() # re-compute commit ids old_commit_ids = pull_request.revisions @@ -783,8 +931,17 @@ class PullRequestModel(BaseModel): target_commit.raw_id, source_commit.raw_id, source_repo, merge=True, pre_load=pre_load) - ancestor_commit_id = source_repo.get_common_ancestor( - source_commit.raw_id, target_commit.raw_id, target_repo) + target_ref = target_commit.raw_id + source_ref = source_commit.raw_id + ancestor_commit_id = target_repo.get_common_ancestor( + target_ref, source_ref, source_repo) + + if not ancestor_commit_id: + raise ValueError( + 'cannot calculate diff info without a common ancestor. ' + 'Make sure both repositories are related, and have a common forking commit.') + + pull_request.common_ancestor_id = ancestor_commit_id pull_request.source_ref = '%s:%s:%s' % ( source_ref_type, source_ref_name, source_commit.raw_id) @@ -885,11 +1042,13 @@ class PullRequestModel(BaseModel): version._last_merge_source_rev = pull_request._last_merge_source_rev version._last_merge_target_rev = pull_request._last_merge_target_rev version.last_merge_status = pull_request.last_merge_status + version.last_merge_metadata = pull_request.last_merge_metadata version.shadow_merge_ref = pull_request.shadow_merge_ref version.merge_rev = pull_request.merge_rev version.reviewer_data = pull_request.reviewer_data version.revisions = pull_request.revisions + version.common_ancestor_id = pull_request.common_ancestor_id version.pull_request = pull_request Session().add(version) Session().flush() @@ -1270,7 +1429,10 @@ class PullRequestModel(BaseModel): email_kwargs=email_kwargs, ) - def delete(self, pull_request, user): + def delete(self, pull_request, user=None): + if not user: + user = getattr(get_current_rhodecode_user(), 'username', None) + pull_request = self.__get_pull_request(pull_request) old_data = pull_request.get_api_data(with_merge_state=False) self._cleanup_merge_workspace(pull_request) @@ -1285,8 +1447,7 @@ class PullRequestModel(BaseModel): pull_request.status = PullRequest.STATUS_CLOSED pull_request.updated_on = datetime.datetime.now() Session().add(pull_request) - self.trigger_pull_request_hook( - pull_request, pull_request.author, 'close') + self.trigger_pull_request_hook(pull_request, pull_request.author, 'close') pr_data = pull_request.get_api_data(with_merge_state=False) self._log_audit_action( @@ -1332,47 +1493,46 @@ class PullRequestModel(BaseModel): ) Session().flush() - events.trigger(events.PullRequestCommentEvent(pull_request, comment)) + + self.trigger_pull_request_hook(pull_request, user, 'comment', + data={'comment': comment}) + # we now calculate the status of pull request again, and based on that # calculation trigger status change. This might happen in cases # that non-reviewer admin closes a pr, which means his vote doesn't # change the status, while if he's a reviewer this might change it. calculated_status = pull_request.calculated_review_status() if old_calculated_status != calculated_status: - self.trigger_pull_request_hook( - pull_request, user, 'review_status_change', - data={'status': calculated_status}) + self.trigger_pull_request_hook(pull_request, user, 'review_status_change', + data={'status': calculated_status}) # finally close the PR - PullRequestModel().close_pull_request( - pull_request.pull_request_id, user) + PullRequestModel().close_pull_request(pull_request.pull_request_id, user) return comment, status - def merge_status(self, pull_request, translator=None, - force_shadow_repo_refresh=False): + def merge_status(self, pull_request, translator=None, force_shadow_repo_refresh=False): _ = translator or get_current_request().translate if not self._is_merge_enabled(pull_request): - return False, _('Server-side pull request merging is disabled.') + return None, False, _('Server-side pull request merging is disabled.') + if pull_request.is_closed(): - return False, _('This pull request is closed.') + return None, False, _('This pull request is closed.') + merge_possible, msg = self._check_repo_requirements( target=pull_request.target_repo, source=pull_request.source_repo, translator=_) if not merge_possible: - return merge_possible, msg + return None, merge_possible, msg try: - resp = self._try_merge( - pull_request, - force_shadow_repo_refresh=force_shadow_repo_refresh) - log.debug("Merge response: %s", resp) - status = resp.possible, resp.merge_status_message + merge_response = self._try_merge( + pull_request, force_shadow_repo_refresh=force_shadow_repo_refresh) + log.debug("Merge response: %s", merge_response) + return merge_response, merge_response.possible, merge_response.merge_status_message except NotImplementedError: - status = False, _('Pull request merging is not supported.') - - return status + return None, False, _('Pull request merging is not supported.') def _check_repo_requirements(self, target, source, translator): """ @@ -1439,6 +1599,9 @@ class PullRequestModel(BaseModel): 'target_ref': pull_request.target_ref_parts, 'source_ref': pull_request.source_ref_parts, } + if pull_request.last_merge_metadata: + metadata.update(pull_request.last_merge_metadata) + if not possible and target_ref.type == 'branch': # NOTE(marcink): case for mercurial multiple heads on branch heads = target_vcs._heads(target_ref.name) @@ -1447,6 +1610,7 @@ class PullRequestModel(BaseModel): metadata.update({ 'heads': heads }) + merge_state = MergeResponse( possible, False, None, pull_request.last_merge_status, metadata=metadata) @@ -1487,6 +1651,8 @@ class PullRequestModel(BaseModel): pull_request.source_ref_parts.commit_id pull_request._last_merge_target_rev = target_reference.commit_id pull_request.last_merge_status = merge_state.failure_reason + pull_request.last_merge_metadata = merge_state.metadata + pull_request.shadow_merge_ref = merge_state.merge_ref Session().add(pull_request) Session().commit() @@ -1627,7 +1793,7 @@ class PullRequestModel(BaseModel): target_commit = source_repo.get_commit( commit_id=safe_str(target_ref_id)) source_commit = source_repo.get_commit( - commit_id=safe_str(source_ref_id)) + commit_id=safe_str(source_ref_id), maybe_unreachable=True) if isinstance(source_repo, Repository): vcs_repo = source_repo.scm_instance() else: @@ -1730,9 +1896,16 @@ class MergeCheck(object): self.review_status = None self.merge_possible = None self.merge_msg = '' + self.merge_response = None self.failed = None self.errors = [] self.error_details = OrderedDict() + self.source_commit = AttributeDict() + self.target_commit = AttributeDict() + + def __repr__(self): + return ''.format( + self.merge_possible, self.failed, self.errors) def push_error(self, error_type, message, error_key, details): self.failed = True @@ -1759,8 +1932,7 @@ class MergeCheck(object): return merge_check # permissions to merge - user_allowed_to_merge = PullRequestModel().check_user_merge( - pull_request, auth_user) + user_allowed_to_merge = PullRequestModel().check_user_merge(pull_request, auth_user) if not user_allowed_to_merge: log.debug("MergeCheck: cannot merge, approval is pending.") @@ -1822,11 +1994,31 @@ class MergeCheck(object): return merge_check # merge possible, here is the filesystem simulation + shadow repo - merge_status, msg = PullRequestModel().merge_status( + merge_response, merge_status, msg = PullRequestModel().merge_status( pull_request, translator=translator, force_shadow_repo_refresh=force_shadow_repo_refresh) + merge_check.merge_possible = merge_status merge_check.merge_msg = msg + merge_check.merge_response = merge_response + + source_ref_id = pull_request.source_ref_parts.commit_id + target_ref_id = pull_request.target_ref_parts.commit_id + + try: + source_commit, target_commit = PullRequestModel().get_flow_commits(pull_request) + merge_check.source_commit.changed = source_ref_id != source_commit.raw_id + merge_check.source_commit.ref_spec = pull_request.source_ref_parts + merge_check.source_commit.current_raw_id = source_commit.raw_id + merge_check.source_commit.previous_raw_id = source_ref_id + + merge_check.target_commit.changed = target_ref_id != target_commit.raw_id + merge_check.target_commit.ref_spec = pull_request.target_ref_parts + merge_check.target_commit.current_raw_id = target_commit.raw_id + merge_check.target_commit.previous_raw_id = target_ref_id + except (SourceRefMissing, TargetRefMissing): + pass + if not merge_status: log.debug("MergeCheck: cannot merge, pull request merge not possible.") merge_check.push_error('warning', msg, cls.MERGE_CHECK, None) diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -1098,7 +1098,7 @@ class ReadmeFinder: self._renderer_extensions = self.RENDERER_TO_EXTENSION.get( default_renderer, []) - def search(self, commit, path='/'): + def search(self, commit, path=u'/'): """ Find a readme in the given `commit`. """ diff --git a/rhodecode/model/repo_group.py b/rhodecode/model/repo_group.py --- a/rhodecode/model/repo_group.py +++ b/rhodecode/model/repo_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/repo_permission.py b/rhodecode/model/repo_permission.py --- a/rhodecode/model/repo_permission.py +++ b/rhodecode/model/repo_permission.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -473,7 +473,7 @@ class ScmModel(BaseModel): # We trigger the post-push action hooks_utils.trigger_post_push_hook( username=user.username, action='push_local', hook_type='post_push', - repo_name=repo_name, repo_alias=repo.alias, commit_ids=[tip.raw_id]) + repo_name=repo_name, repo_type=repo.alias, commit_ids=[tip.raw_id]) return tip def _sanitize_path(self, f_path): @@ -799,7 +799,7 @@ class ScmModel(BaseModel): if trigger_push_hook: hooks_utils.trigger_post_push_hook( username=user.username, action='push_local', - repo_name=repo.repo_name, repo_alias=scm_instance.alias, + repo_name=repo.repo_name, repo_type=scm_instance.alias, hook_type='post_push', commit_ids=[tip.raw_id]) return tip @@ -864,7 +864,7 @@ class ScmModel(BaseModel): if trigger_push_hook: hooks_utils.trigger_post_push_hook( username=user.username, action='push_local', hook_type='post_push', - repo_name=repo.repo_name, repo_alias=scm_instance.alias, + repo_name=repo.repo_name, repo_type=scm_instance.alias, commit_ids=[tip.raw_id]) return tip @@ -926,7 +926,7 @@ class ScmModel(BaseModel): if trigger_push_hook: hooks_utils.trigger_post_push_hook( username=user.username, action='push_local', hook_type='post_push', - repo_name=repo.repo_name, repo_alias=scm_instance.alias, + repo_name=repo.repo_name, repo_type=scm_instance.alias, commit_ids=[tip.raw_id]) return tip diff --git a/rhodecode/model/settings.py b/rhodecode/model/settings.py --- a/rhodecode/model/settings.py +++ b/rhodecode/model/settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/ssh_key.py b/rhodecode/model/ssh_key.py --- a/rhodecode/model/ssh_key.py +++ b/rhodecode/model/ssh_key.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/update.py b/rhodecode/model/update.py --- a/rhodecode/model/update.py +++ b/rhodecode/model/update.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2013-2019 RhodeCode GmbH +# Copyright (C) 2013-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py --- a/rhodecode/model/user.py +++ b/rhodecode/model/user.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -37,17 +37,17 @@ from rhodecode.lib.utils2 import ( AttributeDict, str2bool) from rhodecode.lib.exceptions import ( DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException, - UserOwnsUserGroupsException, NotAllowedToCreateUserError, UserOwnsArtifactsException) + UserOwnsUserGroupsException, NotAllowedToCreateUserError, + UserOwnsPullRequestsException, UserOwnsArtifactsException) from rhodecode.lib.caching_query import FromCache from rhodecode.model import BaseModel -from rhodecode.model.auth_token import AuthTokenModel from rhodecode.model.db import ( _hash_key, true, false, or_, joinedload, User, UserToPerm, UserEmailMap, UserIpMap, UserLog) from rhodecode.model.meta import Session +from rhodecode.model.auth_token import AuthTokenModel from rhodecode.model.repo_group import RepoGroupModel - log = logging.getLogger(__name__) @@ -261,7 +261,7 @@ class UserModel(BaseModel): cur_user = getattr(get_current_rhodecode_user(), 'username', None) from rhodecode.lib.auth import ( - get_crypt_password, check_password, generate_auth_token) + get_crypt_password, check_password) from rhodecode.lib.hooks_base import ( log_create_user, check_allowed_create_user) @@ -443,15 +443,16 @@ class UserModel(BaseModel): log.error(traceback.format_exc()) raise - def _handle_user_repos(self, username, repositories, handle_mode=None): - _superadmin = self.cls.get_first_super_admin() + def _handle_user_repos(self, username, repositories, handle_user, + handle_mode=None): + left_overs = True from rhodecode.model.repo import RepoModel if handle_mode == 'detach': for obj in repositories: - obj.user = _superadmin + obj.user = handle_user # set description we know why we super admin now owns # additional repositories that were orphaned ! obj.description += ' \n::detached repository from deleted user: %s' % (username,) @@ -465,16 +466,16 @@ class UserModel(BaseModel): # if nothing is done we have left overs left return left_overs - def _handle_user_repo_groups(self, username, repository_groups, + def _handle_user_repo_groups(self, username, repository_groups, handle_user, handle_mode=None): - _superadmin = self.cls.get_first_super_admin() + left_overs = True from rhodecode.model.repo_group import RepoGroupModel if handle_mode == 'detach': for r in repository_groups: - r.user = _superadmin + r.user = handle_user # set description we know why we super admin now owns # additional repositories that were orphaned ! r.group_description += ' \n::detached repository group from deleted user: %s' % (username,) @@ -489,8 +490,9 @@ class UserModel(BaseModel): # if nothing is done we have left overs left return left_overs - def _handle_user_user_groups(self, username, user_groups, handle_mode=None): - _superadmin = self.cls.get_first_super_admin() + def _handle_user_user_groups(self, username, user_groups, handle_user, + handle_mode=None): + left_overs = True from rhodecode.model.user_group import UserGroupModel @@ -499,8 +501,8 @@ class UserModel(BaseModel): for r in user_groups: for user_user_group_to_perm in r.user_user_group_to_perm: if user_user_group_to_perm.user.username == username: - user_user_group_to_perm.user = _superadmin - r.user = _superadmin + user_user_group_to_perm.user = handle_user + r.user = handle_user # set description we know why we super admin now owns # additional repositories that were orphaned ! r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,) @@ -514,13 +516,37 @@ class UserModel(BaseModel): # if nothing is done we have left overs left return left_overs - def _handle_user_artifacts(self, username, artifacts, handle_mode=None): - _superadmin = self.cls.get_first_super_admin() + def _handle_user_pull_requests(self, username, pull_requests, handle_user, + handle_mode=None): + left_overs = True + + from rhodecode.model.pull_request import PullRequestModel + + if handle_mode == 'detach': + for pr in pull_requests: + pr.user_id = handle_user.user_id + # set description we know why we super admin now owns + # additional repositories that were orphaned ! + pr.description += ' \n::detached pull requests from deleted user: %s' % (username,) + self.sa.add(pr) + left_overs = False + elif handle_mode == 'delete': + for pr in pull_requests: + PullRequestModel().delete(pr) + + left_overs = False + + # if nothing is done we have left overs left + return left_overs + + def _handle_user_artifacts(self, username, artifacts, handle_user, + handle_mode=None): + left_overs = True if handle_mode == 'detach': for a in artifacts: - a.upload_user = _superadmin + a.upload_user = handle_user # set description we know why we super admin now owns # additional artifacts that were orphaned ! a.file_description += ' \n::detached artifact from deleted user: %s' % (username,) @@ -528,7 +554,8 @@ class UserModel(BaseModel): left_overs = False elif handle_mode == 'delete': from rhodecode.apps.file_store import utils as store_utils - storage = store_utils.get_file_storage(self.request.registry.settings) + request = get_current_request() + storage = store_utils.get_file_storage(request.registry.settings) for a in artifacts: file_uid = a.file_uid storage.delete(file_uid) @@ -540,11 +567,13 @@ class UserModel(BaseModel): return left_overs def delete(self, user, cur_user=None, handle_repos=None, - handle_repo_groups=None, handle_user_groups=None, handle_artifacts=None): + handle_repo_groups=None, handle_user_groups=None, + handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None): from rhodecode.lib.hooks_base import log_delete_user if not cur_user: cur_user = getattr(get_current_rhodecode_user(), 'username', None) + user = self._get_user(user) try: @@ -552,9 +581,11 @@ class UserModel(BaseModel): raise DefaultUserException( u"You can't remove this user since it's" u" crucial for entire application") + handle_user = handle_new_owner or self.cls.get_first_super_admin() + log.debug('New detached objects owner %s', handle_user) left_overs = self._handle_user_repos( - user.username, user.repositories, handle_repos) + user.username, user.repositories, handle_user, handle_repos) if left_overs and user.repositories: repos = [x.repo_name for x in user.repositories] raise UserOwnsReposException( @@ -564,7 +595,7 @@ class UserModel(BaseModel): 'list_repos': ', '.join(repos)}) left_overs = self._handle_user_repo_groups( - user.username, user.repository_groups, handle_repo_groups) + user.username, user.repository_groups, handle_user, handle_repo_groups) if left_overs and user.repository_groups: repo_groups = [x.group_name for x in user.repository_groups] raise UserOwnsRepoGroupsException( @@ -574,7 +605,7 @@ class UserModel(BaseModel): 'list_repo_groups': ', '.join(repo_groups)}) left_overs = self._handle_user_user_groups( - user.username, user.user_groups, handle_user_groups) + user.username, user.user_groups, handle_user, handle_user_groups) if left_overs and user.user_groups: user_groups = [x.users_group_name for x in user.user_groups] raise UserOwnsUserGroupsException( @@ -582,8 +613,17 @@ class UserModel(BaseModel): u'removed. Switch owners or remove those user groups:%s' % (user.username, len(user_groups), ', '.join(user_groups))) + left_overs = self._handle_user_pull_requests( + user.username, user.user_pull_requests, handle_user, handle_pull_requests) + if left_overs and user.user_pull_requests: + pull_requests = ['!{}'.format(x.pull_request_id) for x in user.user_pull_requests] + raise UserOwnsPullRequestsException( + u'user "%s" still owns %s pull requests and cannot be ' + u'removed. Switch owners or remove those pull requests:%s' + % (user.username, len(pull_requests), ', '.join(pull_requests))) + left_overs = self._handle_user_artifacts( - user.username, user.artifacts, handle_artifacts) + user.username, user.artifacts, handle_user, handle_artifacts) if left_overs and user.artifacts: artifacts = [x.file_uid for x in user.artifacts] raise UserOwnsArtifactsException( @@ -878,7 +918,7 @@ class UserModel(BaseModel): end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip())) parsed_ip_range = [] - for index in xrange(int(start_ip), int(end_ip) + 1): + for index in range(int(start_ip), int(end_ip) + 1): new_ip = ipaddress.ip_address(index) parsed_ip_range.append(str(new_ip)) ip_list.extend(parsed_ip_range) diff --git a/rhodecode/model/user_group.py b/rhodecode/model/user_group.py --- a/rhodecode/model/user_group.py +++ b/rhodecode/model/user_group.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/__init__.py b/rhodecode/model/validation_schema/__init__.py --- a/rhodecode/model/validation_schema/__init__.py +++ b/rhodecode/model/validation_schema/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/preparers.py b/rhodecode/model/validation_schema/preparers.py --- a/rhodecode/model/validation_schema/preparers.py +++ b/rhodecode/model/validation_schema/preparers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/__init__.py b/rhodecode/model/validation_schema/schemas/__init__.py --- a/rhodecode/model/validation_schema/schemas/__init__.py +++ b/rhodecode/model/validation_schema/schemas/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/comment_schema.py b/rhodecode/model/validation_schema/schemas/comment_schema.py --- a/rhodecode/model/validation_schema/schemas/comment_schema.py +++ b/rhodecode/model/validation_schema/schemas/comment_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2017-2019 RhodeCode GmbH +# Copyright (C) 2017-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/gist_schema.py b/rhodecode/model/validation_schema/schemas/gist_schema.py --- a/rhodecode/model/validation_schema/schemas/gist_schema.py +++ b/rhodecode/model/validation_schema/schemas/gist_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/integration_schema.py b/rhodecode/model/validation_schema/schemas/integration_schema.py --- a/rhodecode/model/validation_schema/schemas/integration_schema.py +++ b/rhodecode/model/validation_schema/schemas/integration_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/repo_group_schema.py b/rhodecode/model/validation_schema/schemas/repo_group_schema.py --- a/rhodecode/model/validation_schema/schemas/repo_group_schema.py +++ b/rhodecode/model/validation_schema/schemas/repo_group_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/repo_schema.py b/rhodecode/model/validation_schema/schemas/repo_schema.py --- a/rhodecode/model/validation_schema/schemas/repo_schema.py +++ b/rhodecode/model/validation_schema/schemas/repo_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/reviewer_schema.py b/rhodecode/model/validation_schema/schemas/reviewer_schema.py --- a/rhodecode/model/validation_schema/schemas/reviewer_schema.py +++ b/rhodecode/model/validation_schema/schemas/reviewer_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/search_schema.py b/rhodecode/model/validation_schema/schemas/search_schema.py --- a/rhodecode/model/validation_schema/schemas/search_schema.py +++ b/rhodecode/model/validation_schema/schemas/search_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/user_group_schema.py b/rhodecode/model/validation_schema/schemas/user_group_schema.py --- a/rhodecode/model/validation_schema/schemas/user_group_schema.py +++ b/rhodecode/model/validation_schema/schemas/user_group_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/user_schema.py b/rhodecode/model/validation_schema/schemas/user_schema.py --- a/rhodecode/model/validation_schema/schemas/user_schema.py +++ b/rhodecode/model/validation_schema/schemas/user_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/types.py b/rhodecode/model/validation_schema/types.py --- a/rhodecode/model/validation_schema/types.py +++ b/rhodecode/model/validation_schema/types.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/utils.py b/rhodecode/model/validation_schema/utils.py --- a/rhodecode/model/validation_schema/utils.py +++ b/rhodecode/model/validation_schema/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/validators.py b/rhodecode/model/validation_schema/validators.py --- a/rhodecode/model/validation_schema/validators.py +++ b/rhodecode/model/validation_schema/validators.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/widgets.py b/rhodecode/model/validation_schema/widgets.py --- a/rhodecode/model/validation_schema/widgets.py +++ b/rhodecode/model/validation_schema/widgets.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2019 RhodeCode GmbH +# Copyright (C) 2011-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -20,13 +20,40 @@ import logging -import deform import deform.widget +from deform.widget import null, OptGroup, string_types + +log = logging.getLogger(__name__) -log = logging.getLogger(__name__) +def _normalize_choices(values): + result = [] + for item in values: + if isinstance(item, OptGroup): + normalized_options = _normalize_choices(item.options) + result.append(OptGroup(item.label, *normalized_options)) + else: + value, description, help_block = item + if not isinstance(value, string_types): + value = str(value) + result.append((value, description, help_block)) + return result class CodeMirrorWidget(deform.widget.TextAreaWidget): template = 'codemirror' requirements = (('deform', None), ('codemirror', None)) + + +class CheckboxChoiceWidgetDesc(deform.widget.CheckboxChoiceWidget): + template = "checkbox_choice_desc" + + def serialize(self, field, cstruct, **kw): + if cstruct in (null, None): + cstruct = () + readonly = kw.get("readonly", self.readonly) + values = kw.get("values", self.values) + kw["values"] = _normalize_choices(values) + template = readonly and self.readonly_template or self.template + tmpl_values = self.get_template_values(field, cstruct, kw) + return field.renderer(template, **tmpl_values) diff --git a/rhodecode/model/validators.py b/rhodecode/model/validators.py --- a/rhodecode/model/validators.py +++ b/rhodecode/model/validators.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -791,7 +791,7 @@ def ValidPerms(localizer, type_='repo'): # get updates of permissions # (read the existing radio button states) - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() for k, update_value in value.iteritems(): if k.startswith('u_perm_') or k.startswith('g_perm_'): diff --git a/rhodecode/public/css/buttons.less b/rhodecode/public/css/buttons.less --- a/rhodecode/public/css/buttons.less +++ b/rhodecode/public/css/buttons.less @@ -265,7 +265,7 @@ input[type="button"] { .btn-action-switcher-container{ position: absolute; top: 30px; - left: 0px; + left: -82px; } .btn-action-switcher { @@ -378,7 +378,9 @@ input[type="button"] { background-color: transparent; border: none; } + .noselect } + .grid_delete { .action_button { border: none; diff --git a/rhodecode/public/css/code-block.less b/rhodecode/public/css/code-block.less --- a/rhodecode/public/css/code-block.less +++ b/rhodecode/public/css/code-block.less @@ -500,7 +500,7 @@ div.codeblock { img.rendered-binary { height: auto; - width: 100%; + width: auto; } .markdown-block { @@ -594,9 +594,21 @@ div.annotatediv { margin-left: 2px; marg .CodeMirror ::-moz-selection { background: @rchighlightblue; } .code { display: block; border:0px !important; } + .code-highlight, /* TODO: dan: merge codehilite into code-highlight */ +.codehilite { + /*ElasticMatch is custom RhodeCode TAG*/ + + .c-ElasticMatch { + background-color: #faffa6; + padding: 0.2em; + } +} + /* This can be generated with `pygmentize -S default -f html` */ +.code-highlight, .codehilite { + /*ElasticMatch is custom RhodeCode TAG*/ .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;} .hll { background-color: #ffffcc } .c { color: #408080; font-style: italic } /* Comment */ diff --git a/rhodecode/public/css/forms.less b/rhodecode/public/css/forms.less --- a/rhodecode/public/css/forms.less +++ b/rhodecode/public/css/forms.less @@ -402,3 +402,10 @@ input.input-valuedisplay { } } } + +.repo-type-radio input { + margin: 0 0 0 0 +} +.repo-type-radio label { + padding-right: 15px; +} diff --git a/rhodecode/public/css/main.less b/rhodecode/public/css/main.less --- a/rhodecode/public/css/main.less +++ b/rhodecode/public/css/main.less @@ -27,6 +27,8 @@ @import 'panels'; @import 'deform'; @import 'tooltips'; +@import 'sweetalert2'; + //--- BASE ------------------// .noscript-error { @@ -419,8 +421,8 @@ ul.auth_plugins { } .pr-title-input { - width: 80%; - font-size: 1em; + width: 100%; + font-size: 18px; margin: 0 0 4px 0; padding: 0; line-height: 1.7em; @@ -440,6 +442,7 @@ ul.auth_plugins { color: black; opacity: 1; background: #fff; + font-size: 18px; } } @@ -464,9 +467,9 @@ ul.auth_plugins { #pr_open_message { border: @border-thickness solid #fff; border-radius: @border-radius; - padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0; text-align: left; overflow: hidden; + white-space: pre-line; } .pr-details-title { @@ -1483,10 +1486,16 @@ table.integrations { border-bottom: @border-thickness solid @grey5; margin-bottom: @space; } + .reviewers-title { - width: 25%; - min-width: 200px; + width: 25%; + min-width: 200px; + + &.first-panel { + margin-top: 34px; + } } + .reviewers { width: 25%; min-width: 200px; @@ -1787,7 +1796,7 @@ table.integrations { } div.ancestor { - line-height: 33px; + } .cs_icon_td input[type="checkbox"] { @@ -2094,6 +2103,12 @@ BIN_FILENODE = 7 } } +.notice-messages { + .markdown-block, + .rst-block { + padding: 0; + } +} .notifications_buttons{ float: right; @@ -2519,6 +2534,10 @@ h3.files_location{ white-space: nowrap; } + .upload-form { + margin-top: 46px; + } + .location-path { width: -moz-available; /* WebKit-based browsers will ignore this. */ width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */ diff --git a/rhodecode/public/css/navigation.less b/rhodecode/public/css/navigation.less --- a/rhodecode/public/css/navigation.less +++ b/rhodecode/public/css/navigation.less @@ -820,7 +820,53 @@ input { } .menulabel-notice { - border: 1px solid @color5; + padding:7px 10px; + + &.notice-warning { + border: 1px solid @color3; + .notice-color-warning + } + &.notice-error { + border: 1px solid @color5; + .notice-color-error + } + &.notice-info { + border: 1px solid @color1; + .notice-color-info + } +} + +.notice-messages-container { + position: absolute; + top: 45px; +} + +.notice-messages { + display: block; + position: relative; + z-index: 300; + min-width: 500px; + max-width: 500px; + min-height: 100px; + margin-top: 4px; + margin-bottom: 24px; + font-size: 14px; + font-weight: 400; + padding: 8px 0; + background-color: #fff; + border: 1px solid @grey4; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.07); +} + +.notice-color-warning { + color: @color3; +} + +.notice-color-error { color: @color5; } + +.notice-color-info { + color: @color1; +} diff --git a/rhodecode/public/css/rcicons.less b/rhodecode/public/css/rcicons.less --- a/rhodecode/public/css/rcicons.less +++ b/rhodecode/public/css/rcicons.less @@ -269,6 +269,7 @@ .icon-expand-linked { cursor: pointer; color: @grey3; font-size: 14px } .icon-more-linked { cursor: pointer; color: @grey3 } .icon-flag-filled-red { color: @color5 !important; } +.icon-filled-red { color: @color5 !important; } .repo-switcher-dropdown .select2-result-label { .icon-git:before { diff --git a/rhodecode/public/css/select2.less b/rhodecode/public/css/select2.less --- a/rhodecode/public/css/select2.less +++ b/rhodecode/public/css/select2.less @@ -205,7 +205,9 @@ select.select2{height:28px;visibility:hi font-family: @text-regular; color: @grey2; cursor: pointer; + white-space: nowrap; } + &.select2-result-with-children { .select2-result-label { @@ -220,6 +222,7 @@ select.select2{height:28px;visibility:hi font-family: @text-regular; color: @grey2; cursor: pointer; + white-space: nowrap; } } } diff --git a/rhodecode/public/css/sweetalert2.less b/rhodecode/public/css/sweetalert2.less new file mode 100644 --- /dev/null +++ b/rhodecode/public/css/sweetalert2.less @@ -0,0 +1,1544 @@ +.swal2-popup.swal2-toast { + flex-direction: row; + align-items: center; + width: auto; + padding: 0.625em; + overflow-y: hidden; + background: #fff; + box-shadow: none; +} + +.swal2-popup.swal2-toast .swal2-header { + flex-direction: row; +} + +.swal2-popup.swal2-toast .swal2-title { + flex-grow: 1; + justify-content: flex-start; + margin: 0 0.6em; + font-size: 1em; +} + +.swal2-popup.swal2-toast .swal2-footer { + margin: 0.5em 0 0; + padding: 0.5em 0 0; + font-size: 0.8em; +} + +.swal2-popup.swal2-toast .swal2-close { + position: static; + width: 0.8em; + height: 0.8em; + line-height: 0.8; +} + +.swal2-popup.swal2-toast .swal2-content { + justify-content: flex-start; + font-size: 1em; +} + +.swal2-popup.swal2-toast .swal2-icon { + width: 2em; + min-width: 2em; + height: 2em; + margin: 0; +} + +.swal2-popup.swal2-toast .swal2-icon .swal2-icon-content { + display: flex; + align-items: center; + font-size: 1.8em; + font-weight: bold; +} + +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .swal2-popup.swal2-toast .swal2-icon .swal2-icon-content { + font-size: 0.25em; + } +} + +.swal2-popup.swal2-toast .swal2-icon.swal2-success .swal2-success-ring { + width: 2em; + height: 2em; +} + +.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line] { + top: 0.875em; + width: 1.375em; +} + +.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left] { + left: 0.3125em; +} + +.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right] { + right: 0.3125em; +} + +.swal2-popup.swal2-toast .swal2-actions { + flex-basis: auto !important; + width: auto; + height: auto; + margin: 0 0.3125em; +} + +.swal2-popup.swal2-toast .swal2-styled { + margin: 0 0.3125em; + padding: 0.3125em 0.625em; + font-size: 1em; +} + +.swal2-popup.swal2-toast .swal2-styled:focus { + box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(50, 100, 150, 0.4); +} + +.swal2-popup.swal2-toast .swal2-success { + border-color: @alert1; +} + +.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line] { + position: absolute; + width: 1.6em; + height: 3em; + transform: rotate(45deg); + border-radius: 50%; +} + +.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left] { + top: -0.8em; + left: -0.5em; + transform: rotate(-45deg); + transform-origin: 2em 2em; + border-radius: 4em 0 0 4em; +} + +.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right] { + top: -0.25em; + left: 0.9375em; + transform-origin: 0 1.5em; + border-radius: 0 4em 4em 0; +} + +.swal2-popup.swal2-toast .swal2-success .swal2-success-ring { + width: 2em; + height: 2em; +} + +.swal2-popup.swal2-toast .swal2-success .swal2-success-fix { + top: 0; + left: 0.4375em; + width: 0.4375em; + height: 2.6875em; +} + +.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line] { + height: 0.3125em; +} + +.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip] { + top: 1.125em; + left: 0.1875em; + width: 0.75em; +} + +.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=long] { + top: 0.9375em; + right: 0.1875em; + width: 1.375em; +} + +.swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip { + -webkit-animation: swal2-toast-animate-success-line-tip 0.75s; + animation: swal2-toast-animate-success-line-tip 0.75s; +} + +.swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long { + -webkit-animation: swal2-toast-animate-success-line-long 0.75s; + animation: swal2-toast-animate-success-line-long 0.75s; +} + +.swal2-popup.swal2-toast.swal2-show { + -webkit-animation: swal2-toast-show 0.5s; + animation: swal2-toast-show 0.5s; +} + +.swal2-popup.swal2-toast.swal2-hide { + -webkit-animation: swal2-toast-hide 0.1s forwards; + animation: swal2-toast-hide 0.1s forwards; +} + +.swal2-container { + display: flex; + position: fixed; + z-index: 1060; + top: 0; + right: 0; + bottom: 0; + left: 0; + flex-direction: row; + align-items: center; + justify-content: center; + padding: 0.625em; + overflow-x: hidden; + transition: background-color 0.1s; + -webkit-overflow-scrolling: touch; +} + +.swal2-container.swal2-backdrop-show, .swal2-container.swal2-noanimation { + background: rgba(0, 0, 0, 0.4); +} + +.swal2-container.swal2-backdrop-hide { + background: transparent !important; +} + +.swal2-container.swal2-top { + align-items: flex-start; +} + +.swal2-container.swal2-top-start, .swal2-container.swal2-top-left { + align-items: flex-start; + justify-content: flex-start; +} + +.swal2-container.swal2-top-end, .swal2-container.swal2-top-right { + align-items: flex-start; + justify-content: flex-end; +} + +.swal2-container.swal2-center { + align-items: center; +} + +.swal2-container.swal2-center-start, .swal2-container.swal2-center-left { + align-items: center; + justify-content: flex-start; +} + +.swal2-container.swal2-center-end, .swal2-container.swal2-center-right { + align-items: center; + justify-content: flex-end; +} + +.swal2-container.swal2-bottom { + align-items: flex-end; +} + +.swal2-container.swal2-bottom-start, .swal2-container.swal2-bottom-left { + align-items: flex-end; + justify-content: flex-start; +} + +.swal2-container.swal2-bottom-end, .swal2-container.swal2-bottom-right { + align-items: flex-end; + justify-content: flex-end; +} + +.swal2-container.swal2-bottom > :first-child, +.swal2-container.swal2-bottom-start > :first-child, +.swal2-container.swal2-bottom-left > :first-child, +.swal2-container.swal2-bottom-end > :first-child, +.swal2-container.swal2-bottom-right > :first-child { + margin-top: auto; +} + +.swal2-container.swal2-grow-fullscreen > .swal2-modal { + display: flex !important; + flex: 1; + align-self: stretch; + justify-content: center; +} + +.swal2-container.swal2-grow-row > .swal2-modal { + display: flex !important; + flex: 1; + align-content: center; + justify-content: center; +} + +.swal2-container.swal2-grow-column { + flex: 1; + flex-direction: column; +} + +.swal2-container.swal2-grow-column.swal2-top, .swal2-container.swal2-grow-column.swal2-center, .swal2-container.swal2-grow-column.swal2-bottom { + align-items: center; +} + +.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 { + align-items: flex-start; +} + +.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 { + align-items: flex-end; +} + +.swal2-container.swal2-grow-column > .swal2-modal { + display: flex !important; + flex: 1; + align-content: center; + justify-content: center; +} + +.swal2-container.swal2-no-transition { + transition: none !important; +} + +.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 { + margin: auto; +} + +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .swal2-container .swal2-modal { + margin: 0 !important; + } +} + +.swal2-popup { + display: none; + position: relative; + box-sizing: border-box; + flex-direction: column; + justify-content: center; + width: 38em; + max-width: 100%; + padding: 1.25em; + border: none; + border-radius: 0; + background: #fff; + font-family: inherit; + font-size: 1rem; +} + +.swal2-popup:focus { + outline: none; +} + +.swal2-popup.swal2-loading { + overflow-y: hidden; +} + +.swal2-header { + display: flex; + flex-direction: column; + align-items: center; +} + +.swal2-title { + position: relative; + max-width: 100%; + margin: 0 0 0.4em; + padding: 0; + color: #595959; + font-size: 1.875em; + font-weight: 600; + text-align: center; + text-transform: none; + word-wrap: break-word; +} + +.swal2-actions { + display: flex; + z-index: 1; + flex-wrap: wrap; + align-items: center; + justify-content: center; + width: 100%; + margin: 1.25em auto 0; +} + +.swal2-actions:not(.swal2-loading) .swal2-styled[disabled] { + opacity: 0.4; +} + +.swal2-actions:not(.swal2-loading) .swal2-styled:hover { + background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)); +} + +.swal2-actions:not(.swal2-loading) .swal2-styled:active { + background-image: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)); +} + +.swal2-actions.swal2-loading .swal2-styled.swal2-confirm { + box-sizing: border-box; + width: 2.5em; + height: 2.5em; + margin: 0.46875em; + padding: 0; + -webkit-animation: swal2-rotate-loading 1.5s linear 0s infinite normal; + animation: swal2-rotate-loading 1.5s linear 0s infinite normal; + border: 0.25em solid transparent; + border-radius: 100%; + border-color: transparent; + background-color: transparent !important; + color: transparent !important; + cursor: default; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.swal2-actions.swal2-loading .swal2-styled.swal2-cancel { + margin-right: 30px; + margin-left: 30px; +} + +.swal2-actions.swal2-loading :not(.swal2-styled).swal2-confirm::after { + content: ""; + display: inline-block; + width: 15px; + height: 15px; + margin-left: 5px; + -webkit-animation: swal2-rotate-loading 1.5s linear 0s infinite normal; + animation: swal2-rotate-loading 1.5s linear 0s infinite normal; + border: 3px solid #999999; + border-radius: 50%; + border-right-color: transparent; + box-shadow: 1px 1px 1px #fff; +} + +.swal2-styled { + margin: 0.3125em; + padding: 0.625em 2em; + box-shadow: none; + font-weight: 500; +} + +.swal2-styled:not([disabled]) { + cursor: pointer; +} + +.swal2-styled.swal2-confirm { + border: 0; + border-radius: 0; + background: initial; + background-color: @alert4; + color: #fff; + font-size: 1.0625em; +} + +.swal2-styled.swal2-cancel { + border: 0; + border-radius: 0; + background: initial; + background-color: #aaa; + color: #fff; + font-size: 1.0625em; +} + +.swal2-styled:focus { + outline: none; + box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(50, 100, 150, 0.4); +} + +.swal2-styled::-moz-focus-inner { + border: 0; +} + +.swal2-footer { + justify-content: center; + margin: 1.25em 0 0; + padding: 1em 0 0; + border-top: 1px solid #eee; + color: #545454; + font-size: 1em; +} + +.swal2-timer-progress-bar-container { + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 0.25em; + overflow: hidden; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.swal2-timer-progress-bar { + width: 100%; + height: 0.25em; + background: rgba(0, 0, 0, 0.2); +} + +.swal2-image { + max-width: 100%; + margin: 1.25em auto; +} + +.swal2-close { + position: absolute; + z-index: 2; + /* 1617 */ + top: 0; + right: 0; + align-items: center; + justify-content: center; + width: 1.2em; + height: 1.2em; + padding: 0; + overflow: hidden; + transition: color 0.1s ease-out; + border: none; + border-radius: 0; + background: transparent; + color: #cccccc; + font-family: serif; + font-size: 2.5em; + line-height: 1.2; + cursor: pointer; +} + +.swal2-close:hover { + transform: none; + background: transparent; + color: #f27474; +} + +.swal2-close::-moz-focus-inner { + border: 0; +} + +.swal2-content { + z-index: 1; + justify-content: center; + margin: 0; + padding: 0; + color: #545454; + font-size: 1.125em; + font-weight: normal; + line-height: normal; + text-align: center; + word-wrap: break-word; +} + +.swal2-input, +.swal2-file, +.swal2-textarea, +.swal2-select, +.swal2-radio, +.swal2-checkbox { + margin: 1em auto; +} + +.swal2-input, +.swal2-file, +.swal2-textarea { + box-sizing: border-box; + width: 100%; + transition: initial; + border: 1px solid #d9d9d9; + border-radius: 0; + background: inherit; + box-shadow: none; + color: inherit; + font-size: 1.125em; +} + +.swal2-input.swal2-inputerror, +.swal2-file.swal2-inputerror, +.swal2-textarea.swal2-inputerror { + border-color: #f27474 !important; + box-shadow: 0 0 2px #f27474 !important; +} + +.swal2-input:focus, +.swal2-file:focus, +.swal2-textarea:focus { + border: 1px solid #b4dbed; + outline: none; + box-shadow: 0 0 3px #c4e6f5; +} + +.swal2-input::-webkit-input-placeholder, .swal2-file::-webkit-input-placeholder, .swal2-textarea::-webkit-input-placeholder { + color: #cccccc; +} + +.swal2-input::-moz-placeholder, .swal2-file::-moz-placeholder, .swal2-textarea::-moz-placeholder { + color: #cccccc; +} + +.swal2-input:-ms-input-placeholder, .swal2-file:-ms-input-placeholder, .swal2-textarea:-ms-input-placeholder { + color: #cccccc; +} + +.swal2-input::-ms-input-placeholder, .swal2-file::-ms-input-placeholder, .swal2-textarea::-ms-input-placeholder { + color: #cccccc; +} + +.swal2-input::placeholder, +.swal2-file::placeholder, +.swal2-textarea::placeholder { + color: #cccccc; +} + +.swal2-range { + margin: 1em auto; + background: #fff; +} + +.swal2-range input { + width: 80%; +} + +.swal2-range output { + width: 20%; + color: inherit; + font-weight: 600; + text-align: center; +} + +.swal2-range input, +.swal2-range output { + height: 2.625em; + padding: 0; + font-size: 1.125em; + line-height: 2.625em; +} + +.swal2-input { + height: 2.625em; + padding: 0 0.75em; +} + +.swal2-input[type=number] { + max-width: 10em; +} + +.swal2-file { + background: inherit; + font-size: 1.125em; +} + +.swal2-textarea { + height: 6.75em; + padding: 0.75em; +} + +.swal2-select { + min-width: 50%; + max-width: 100%; + padding: 0.375em 0.625em; + background: inherit; + color: inherit; + font-size: 1.125em; +} + +.swal2-radio, +.swal2-checkbox { + align-items: center; + justify-content: center; + background: #fff; + color: inherit; +} + +.swal2-radio label, +.swal2-checkbox label { + margin: 0 0.6em; + font-size: 1.125em; +} + +.swal2-radio input, +.swal2-checkbox input { + margin: 0 0.4em; +} + +.swal2-validation-message { + display: none; + align-items: center; + justify-content: center; + padding: 0.625em; + overflow: hidden; + background: #f0f0f0; + color: #666666; + font-size: 1em; + font-weight: 300; +} + +.swal2-validation-message::before { + content: "!"; + display: inline-block; + width: 1.5em; + min-width: 1.5em; + height: 1.5em; + margin: 0 0.625em; + border-radius: 50%; + background-color: #f27474; + color: #fff; + font-weight: 600; + line-height: 1.5em; + text-align: center; +} + +.swal2-icon { + position: relative; + box-sizing: content-box; + justify-content: center; + width: 5em; + height: 5em; + margin: 1.25em auto 1.875em; + border: 0.25em solid transparent; + border-radius: 50%; + font-family: inherit; + line-height: 5em; + cursor: default; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.swal2-icon .swal2-icon-content { + display: flex; + align-items: center; + font-size: 3.75em; +} + +.swal2-icon.swal2-error { + border-color: @alert2; + color: @alert2; +} + +.swal2-icon.swal2-error .swal2-x-mark { + position: relative; + flex-grow: 1; +} + +.swal2-icon.swal2-error [class^=swal2-x-mark-line] { + display: block; + position: absolute; + top: 2.3125em; + width: 2.9375em; + height: 0.3125em; + border-radius: 0.125em; + background-color: @alert2; +} + +.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left] { + left: 1.0625em; + transform: rotate(45deg); +} + +.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right] { + right: 1em; + transform: rotate(-45deg); +} + +.swal2-icon.swal2-error.swal2-icon-show { + //-webkit-animation: swal2-animate-error-icon 0.5s; + // animation: swal2-animate-error-icon 0.5s; +} + +.swal2-icon.swal2-error.swal2-icon-show .swal2-x-mark { + //-webkit-animation: swal2-animate-error-x-mark 0.5s; + // animation: swal2-animate-error-x-mark 0.5s; +} + +.swal2-icon.swal2-warning { + border-color: @alert3; + color: @alert3; +} + +.swal2-icon.swal2-info { + border-color: #9de0f6; + color: #3fc3ee; +} + +.swal2-icon.swal2-question { + border-color: #c9dae1; + color: #87adbd; +} + +.swal2-icon.swal2-success { + border-color: @alert1; + color: @alert1; +} + +.swal2-icon.swal2-success [class^=swal2-success-circular-line] { + position: absolute; + width: 3.75em; + height: 7.5em; + transform: rotate(45deg); + border-radius: 50%; +} + +.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=left] { + top: -0.4375em; + left: -2.0635em; + transform: rotate(-45deg); + transform-origin: 3.75em 3.75em; + border-radius: 7.5em 0 0 7.5em; +} + +.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=right] { + top: -0.6875em; + left: 1.875em; + transform: rotate(-45deg); + transform-origin: 0 3.75em; + border-radius: 0 7.5em 7.5em 0; +} + +.swal2-icon.swal2-success .swal2-success-ring { + position: absolute; + z-index: 2; + top: -0.25em; + left: -0.25em; + box-sizing: content-box; + width: 100%; + height: 100%; + border: 0.25em solid rgba(165, 220, 134, 0.3); + border-radius: 50%; +} + +.swal2-icon.swal2-success .swal2-success-fix { + position: absolute; + z-index: 1; + top: 0.5em; + left: 1.625em; + width: 0.4375em; + height: 5.625em; + transform: rotate(-45deg); +} + +.swal2-icon.swal2-success [class^=swal2-success-line] { + display: block; + position: absolute; + z-index: 2; + height: 0.3125em; + border-radius: 0.125em; + background-color: @alert1; +} + +.swal2-icon.swal2-success [class^=swal2-success-line][class$=tip] { + top: 2.875em; + left: 0.8125em; + width: 1.5625em; + transform: rotate(45deg); +} + +.swal2-icon.swal2-success [class^=swal2-success-line][class$=long] { + top: 2.375em; + right: 0.5em; + width: 2.9375em; + transform: rotate(-45deg); +} + +.swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-tip { + -webkit-animation: swal2-animate-success-line-tip 0.75s; + animation: swal2-animate-success-line-tip 0.75s; +} + +.swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-long { + -webkit-animation: swal2-animate-success-line-long 0.75s; + animation: swal2-animate-success-line-long 0.75s; +} + +.swal2-icon.swal2-success.swal2-icon-show .swal2-success-circular-line-right { + -webkit-animation: swal2-rotate-success-circular-line 4.25s ease-in; + animation: swal2-rotate-success-circular-line 4.25s ease-in; +} + +.swal2-progress-steps { + align-items: center; + margin: 0 0 1.25em; + padding: 0; + background: inherit; + font-weight: 600; +} + +.swal2-progress-steps li { + display: inline-block; + position: relative; +} + +.swal2-progress-steps .swal2-progress-step { + z-index: 20; + width: 2em; + height: 2em; + border-radius: 2em; + background: #3085d6; + color: #fff; + line-height: 2em; + text-align: center; +} + +.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step { + background: #3085d6; +} + +.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step ~ .swal2-progress-step { + background: #add8e6; + color: #fff; +} + +.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step ~ .swal2-progress-step-line { + background: #add8e6; +} + +.swal2-progress-steps .swal2-progress-step-line { + z-index: 10; + width: 2.5em; + height: 0.4em; + margin: 0 -1px; + background: #3085d6; +} + +[class^=swal2] { + -webkit-tap-highlight-color: transparent; +} + +.swal2-show { + -webkit-animation: swal2-show 0.3s; + animation: swal2-show 0.3s; +} + +.swal2-hide { + -webkit-animation: swal2-hide 0.15s forwards; + animation: swal2-hide 0.15s forwards; +} + +.swal2-noanimation { + transition: none; +} + +.swal2-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +.swal2-rtl .swal2-close { + right: auto; + left: 0; +} + +.swal2-rtl .swal2-timer-progress-bar { + right: 0; + left: auto; +} + +@supports (-ms-accelerator: true) { + .swal2-range input { + width: 100% !important; + } + + .swal2-range output { + display: none; + } +} + +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .swal2-range input { + width: 100% !important; + } + + .swal2-range output { + display: none; + } +} + +@-moz-document url-prefix() { + .swal2-close:focus { + outline: 2px solid rgba(50, 100, 150, 0.4); + } +} + +@-webkit-keyframes swal2-toast-show { + 0% { + transform: translateY(-0.625em) rotateZ(2deg); + } + 33% { + transform: translateY(0) rotateZ(-2deg); + } + 66% { + transform: translateY(0.3125em) rotateZ(2deg); + } + 100% { + transform: translateY(0) rotateZ(0deg); + } +} + +@keyframes swal2-toast-show { + 0% { + transform: translateY(-0.625em) rotateZ(2deg); + } + 33% { + transform: translateY(0) rotateZ(-2deg); + } + 66% { + transform: translateY(0.3125em) rotateZ(2deg); + } + 100% { + transform: translateY(0) rotateZ(0deg); + } +} + +@-webkit-keyframes swal2-toast-hide { + 100% { + transform: rotateZ(1deg); + opacity: 0; + } +} + +@keyframes swal2-toast-hide { + 100% { + transform: rotateZ(1deg); + opacity: 0; + } +} + +@-webkit-keyframes swal2-toast-animate-success-line-tip { + 0% { + top: 0.5625em; + left: 0.0625em; + width: 0; + } + 54% { + top: 0.125em; + left: 0.125em; + width: 0; + } + 70% { + top: 0.625em; + left: -0.25em; + width: 1.625em; + } + 84% { + top: 1.0625em; + left: 0.75em; + width: 0.5em; + } + 100% { + top: 1.125em; + left: 0.1875em; + width: 0.75em; + } +} + +@keyframes swal2-toast-animate-success-line-tip { + 0% { + top: 0.5625em; + left: 0.0625em; + width: 0; + } + 54% { + top: 0.125em; + left: 0.125em; + width: 0; + } + 70% { + top: 0.625em; + left: -0.25em; + width: 1.625em; + } + 84% { + top: 1.0625em; + left: 0.75em; + width: 0.5em; + } + 100% { + top: 1.125em; + left: 0.1875em; + width: 0.75em; + } +} + +@-webkit-keyframes swal2-toast-animate-success-line-long { + 0% { + top: 1.625em; + right: 1.375em; + width: 0; + } + 65% { + top: 1.25em; + right: 0.9375em; + width: 0; + } + 84% { + top: 0.9375em; + right: 0; + width: 1.125em; + } + 100% { + top: 0.9375em; + right: 0.1875em; + width: 1.375em; + } +} + +@keyframes swal2-toast-animate-success-line-long { + 0% { + top: 1.625em; + right: 1.375em; + width: 0; + } + 65% { + top: 1.25em; + right: 0.9375em; + width: 0; + } + 84% { + top: 0.9375em; + right: 0; + width: 1.125em; + } + 100% { + top: 0.9375em; + right: 0.1875em; + width: 1.375em; + } +} + +@-webkit-keyframes swal2-show { + 0% { + transform: scale(0.7); + } + 45% { + transform: scale(1.05); + } + 80% { + transform: scale(0.95); + } + 100% { + transform: scale(1); + } +} + +@keyframes swal2-show { + 0% { + transform: scale(0.7); + } + 45% { + transform: scale(1.05); + } + 80% { + transform: scale(0.95); + } + 100% { + transform: scale(1); + } +} + +@-webkit-keyframes swal2-hide { + 0% { + transform: scale(1); + opacity: 1; + } + 100% { + transform: scale(0.5); + opacity: 0; + } +} + +@keyframes swal2-hide { + 0% { + transform: scale(1); + opacity: 1; + } + 100% { + transform: scale(0.5); + opacity: 0; + } +} + +@-webkit-keyframes swal2-animate-success-line-tip { + 0% { + top: 1.1875em; + left: 0.0625em; + width: 0; + } + 54% { + top: 1.0625em; + left: 0.125em; + width: 0; + } + 70% { + top: 2.1875em; + left: -0.375em; + width: 3.125em; + } + 84% { + top: 3em; + left: 1.3125em; + width: 1.0625em; + } + 100% { + top: 2.8125em; + left: 0.8125em; + width: 1.5625em; + } +} + +@keyframes swal2-animate-success-line-tip { + 0% { + top: 1.1875em; + left: 0.0625em; + width: 0; + } + 54% { + top: 1.0625em; + left: 0.125em; + width: 0; + } + 70% { + top: 2.1875em; + left: -0.375em; + width: 3.125em; + } + 84% { + top: 3em; + left: 1.3125em; + width: 1.0625em; + } + 100% { + top: 2.8125em; + left: 0.8125em; + width: 1.5625em; + } +} + +@-webkit-keyframes swal2-animate-success-line-long { + 0% { + top: 3.375em; + right: 2.875em; + width: 0; + } + 65% { + top: 3.375em; + right: 2.875em; + width: 0; + } + 84% { + top: 2.1875em; + right: 0; + width: 3.4375em; + } + 100% { + top: 2.375em; + right: 0.5em; + width: 2.9375em; + } +} + +@keyframes swal2-animate-success-line-long { + 0% { + top: 3.375em; + right: 2.875em; + width: 0; + } + 65% { + top: 3.375em; + right: 2.875em; + width: 0; + } + 84% { + top: 2.1875em; + right: 0; + width: 3.4375em; + } + 100% { + top: 2.375em; + right: 0.5em; + width: 2.9375em; + } +} + +@-webkit-keyframes swal2-rotate-success-circular-line { + 0% { + transform: rotate(-45deg); + } + 5% { + transform: rotate(-45deg); + } + 12% { + transform: rotate(-405deg); + } + 100% { + transform: rotate(-405deg); + } +} + +@keyframes swal2-rotate-success-circular-line { + 0% { + transform: rotate(-45deg); + } + 5% { + transform: rotate(-45deg); + } + 12% { + transform: rotate(-405deg); + } + 100% { + transform: rotate(-405deg); + } +} + +@-webkit-keyframes swal2-animate-error-x-mark { + 0% { + margin-top: 1.625em; + transform: scale(0.4); + opacity: 0; + } + 50% { + margin-top: 1.625em; + transform: scale(0.4); + opacity: 0; + } + 80% { + margin-top: -0.375em; + transform: scale(1.15); + } + 100% { + margin-top: 0; + transform: scale(1); + opacity: 1; + } +} + +@keyframes swal2-animate-error-x-mark { + 0% { + margin-top: 1.625em; + transform: scale(0.4); + opacity: 0; + } + 50% { + margin-top: 1.625em; + transform: scale(0.4); + opacity: 0; + } + 80% { + margin-top: -0.375em; + transform: scale(1.15); + } + 100% { + margin-top: 0; + transform: scale(1); + opacity: 1; + } +} + +@-webkit-keyframes swal2-animate-error-icon { + 0% { + transform: rotateX(100deg); + opacity: 0; + } + 100% { + transform: rotateX(0deg); + opacity: 1; + } +} + +@keyframes swal2-animate-error-icon { + 0% { + transform: rotateX(100deg); + opacity: 0; + } + 100% { + transform: rotateX(0deg); + opacity: 1; + } +} + +@-webkit-keyframes swal2-rotate-loading { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +@keyframes swal2-rotate-loading { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) { + overflow: hidden; +} + +body.swal2-height-auto { + height: auto !important; +} + +body.swal2-no-backdrop .swal2-container { + top: auto; + right: auto; + bottom: auto; + left: auto; + max-width: calc(100% - 0.625em * 2); + background-color: transparent !important; +} + +body.swal2-no-backdrop .swal2-container > .swal2-modal { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); +} + +body.swal2-no-backdrop .swal2-container.swal2-top { + top: 0; + left: 50%; + transform: translateX(-50%); +} + +body.swal2-no-backdrop .swal2-container.swal2-top-start, body.swal2-no-backdrop .swal2-container.swal2-top-left { + top: 0; + left: 0; +} + +body.swal2-no-backdrop .swal2-container.swal2-top-end, body.swal2-no-backdrop .swal2-container.swal2-top-right { + top: 0; + right: 0; +} + +body.swal2-no-backdrop .swal2-container.swal2-center { + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +body.swal2-no-backdrop .swal2-container.swal2-center-start, body.swal2-no-backdrop .swal2-container.swal2-center-left { + top: 50%; + left: 0; + transform: translateY(-50%); +} + +body.swal2-no-backdrop .swal2-container.swal2-center-end, body.swal2-no-backdrop .swal2-container.swal2-center-right { + top: 50%; + right: 0; + transform: translateY(-50%); +} + +body.swal2-no-backdrop .swal2-container.swal2-bottom { + bottom: 0; + left: 50%; + transform: translateX(-50%); +} + +body.swal2-no-backdrop .swal2-container.swal2-bottom-start, body.swal2-no-backdrop .swal2-container.swal2-bottom-left { + bottom: 0; + left: 0; +} + +body.swal2-no-backdrop .swal2-container.swal2-bottom-end, body.swal2-no-backdrop .swal2-container.swal2-bottom-right { + right: 0; + bottom: 0; +} + +@media print { + body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) { + overflow-y: scroll !important; + } + + body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) > [aria-hidden=true] { + display: none; + } + + body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) .swal2-container { + position: static !important; + } +} + +body.swal2-toast-shown .swal2-container { + background-color: transparent; +} + +body.swal2-toast-shown .swal2-container.swal2-top { + top: 0; + right: auto; + bottom: auto; + left: 50%; + transform: translateX(-50%); +} + +body.swal2-toast-shown .swal2-container.swal2-top-end, body.swal2-toast-shown .swal2-container.swal2-top-right { + top: 0; + right: 0; + bottom: auto; + left: auto; +} + +body.swal2-toast-shown .swal2-container.swal2-top-start, body.swal2-toast-shown .swal2-container.swal2-top-left { + top: 0; + right: auto; + bottom: auto; + left: 0; +} + +body.swal2-toast-shown .swal2-container.swal2-center-start, body.swal2-toast-shown .swal2-container.swal2-center-left { + top: 50%; + right: auto; + bottom: auto; + left: 0; + transform: translateY(-50%); +} + +body.swal2-toast-shown .swal2-container.swal2-center { + top: 50%; + right: auto; + bottom: auto; + left: 50%; + transform: translate(-50%, -50%); +} + +body.swal2-toast-shown .swal2-container.swal2-center-end, body.swal2-toast-shown .swal2-container.swal2-center-right { + top: 50%; + right: 0; + bottom: auto; + left: auto; + transform: translateY(-50%); +} + +body.swal2-toast-shown .swal2-container.swal2-bottom-start, body.swal2-toast-shown .swal2-container.swal2-bottom-left { + top: auto; + right: auto; + bottom: 0; + left: 0; +} + +body.swal2-toast-shown .swal2-container.swal2-bottom { + top: auto; + right: auto; + bottom: 0; + left: 50%; + transform: translateX(-50%); +} + +body.swal2-toast-shown .swal2-container.swal2-bottom-end, body.swal2-toast-shown .swal2-container.swal2-bottom-right { + top: auto; + right: 0; + bottom: 0; + left: auto; +} + +body.swal2-toast-column .swal2-toast { + flex-direction: column; + align-items: stretch; +} + +body.swal2-toast-column .swal2-toast .swal2-actions { + flex: 1; + align-self: stretch; + height: 2.2em; + margin-top: 0.3125em; +} + +body.swal2-toast-column .swal2-toast .swal2-loading { + justify-content: center; +} + +body.swal2-toast-column .swal2-toast .swal2-input { + height: 2em; + margin: 0.3125em auto; + font-size: 1em; +} + +body.swal2-toast-column .swal2-toast .swal2-validation-message { + font-size: 1em; +} \ No newline at end of file diff --git a/rhodecode/public/css/tables.less b/rhodecode/public/css/tables.less --- a/rhodecode/public/css/tables.less +++ b/rhodecode/public/css/tables.less @@ -352,7 +352,7 @@ table.dataTable { margin-bottom: @padding; table.rctable td:first-child { - width: 340px; + width: 120px; } } diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -110,6 +110,8 @@ function registerRCRoutes() { pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']); pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']); pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']); + pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']); + pyroutes.register('edit_user_auth_tokens_view', '/_admin/users/%(user_id)s/edit/auth_tokens/view', ['user_id']); pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']); pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']); pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']); @@ -203,6 +205,7 @@ function registerRCRoutes() { pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); + pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); 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 @@ function registerRCRoutes() { pyroutes.register('my_account_update', '/_admin/my_account/update', []); pyroutes.register('my_account_password', '/_admin/my_account/password', []); pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []); + pyroutes.register('my_account_auth_tokens_view', '/_admin/my_account/auth_tokens/view', []); pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []); pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []); pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []); diff --git a/rhodecode/public/js/src/ejs_templates/utils.js b/rhodecode/public/js/src/ejs_templates/utils.js --- a/rhodecode/public/js/src/ejs_templates/utils.js +++ b/rhodecode/public/js/src/ejs_templates/utils.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/i18n_utils.js b/rhodecode/public/js/src/i18n_utils.js --- a/rhodecode/public/js/src/i18n_utils.js +++ b/rhodecode/public/js/src/i18n_utils.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2016-2019 RhodeCode GmbH +// # Copyright (C) 2016-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 @@ -22,7 +22,7 @@ var _gettext = function (s) { if (_TM.hasOwnProperty(s)) { return _TM[s]; } - i18nLog.error( + i18nLog.warning( 'String `' + s + '` was requested but cannot be ' + 'found in translation table'); return s @@ -34,3 +34,5 @@ var _ngettext = function (singular, plur } return _gettext(plural) }; + + diff --git a/rhodecode/public/js/src/logging.js b/rhodecode/public/js/src/logging.js --- a/rhodecode/public/js/src/logging.js +++ b/rhodecode/public/js/src/logging.js @@ -113,6 +113,10 @@ this.invoke(Logger.WARN, arguments); }, + warning: function () { + this.invoke(Logger.WARN, arguments); + }, + error: function () { this.invoke(Logger.ERROR, arguments); }, @@ -151,6 +155,7 @@ L.timeEnd = bind(globalLogger, globalLogger.timeEnd); L.info = bind(globalLogger, globalLogger.info); L.warn = bind(globalLogger, globalLogger.warn); + L.warning = bind(globalLogger, globalLogger.warning); L.error = bind(globalLogger, globalLogger.error); // Don't forget the convenience alias! diff --git a/rhodecode/public/js/src/plugins/jquery.autocomplete.js b/rhodecode/public/js/src/plugins/jquery.autocomplete.js --- a/rhodecode/public/js/src/plugins/jquery.autocomplete.js +++ b/rhodecode/public/js/src/plugins/jquery.autocomplete.js @@ -564,13 +564,15 @@ $.extend(ajaxSettings, options.ajaxSettings); - that.currentRequest = $.ajax(ajaxSettings).done(function (data) { + that.currentRequest = $.ajax(ajaxSettings) + .done(function (data) { var result; that.currentRequest = null; result = options.transformResult(data); that.processResponse(result, query, cacheKey); options.onSearchComplete.call(that.element, query, result.suggestions); - }).fail(function (jqXHR, textStatus, errorThrown) { + }) + .fail(function (jqXHR, textStatus, errorThrown) { options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown); }); } diff --git a/rhodecode/public/js/src/rhodecode.js b/rhodecode/public/js/src/rhodecode.js --- a/rhodecode/public/js/src/rhodecode.js +++ b/rhodecode/public/js/src/rhodecode.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/appenlight.js b/rhodecode/public/js/src/rhodecode/appenlight.js --- a/rhodecode/public/js/src/rhodecode/appenlight.js +++ b/rhodecode/public/js/src/rhodecode/appenlight.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/changelog.js b/rhodecode/public/js/src/rhodecode/changelog.js --- a/rhodecode/public/js/src/rhodecode/changelog.js +++ b/rhodecode/public/js/src/rhodecode/changelog.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2016-2019 RhodeCode GmbH +// # Copyright (C) 2016-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/codemirror.js b/rhodecode/public/js/src/rhodecode/codemirror.js --- a/rhodecode/public/js/src/rhodecode/codemirror.js +++ b/rhodecode/public/js/src/rhodecode/codemirror.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/comments.js b/rhodecode/public/js/src/rhodecode/comments.js --- a/rhodecode/public/js/src/rhodecode/comments.js +++ b/rhodecode/public/js/src/rhodecode/comments.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 @@ -347,11 +347,10 @@ var _submitAjaxPOST = function(url, post self.globalSubmitSuccessCallback(); }; - var submitFailCallback = function(data) { - alert( - "Error while submitting comment.\n" + - "Error code {0} ({1}).".format(data.status, data.statusText) - ); + var submitFailCallback = function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while submitting comment.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); self.resetCommentFormState(text); }; self.submitAjaxPOST( @@ -447,11 +446,11 @@ var _submitAjaxPOST = function(url, post $(self.previewContainer).show(); // by default we reset state of comment preserving the text - var previewFailCallback = function(data){ - alert( - "Error while preview of comment.\n" + - "Error code {0} ({1}).".format(data.status, data.statusText) - ); + var previewFailCallback = function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while preview of comment.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); + self.resetCommentFormState(text) }; self.submitAjaxPOST( @@ -556,10 +555,7 @@ var CommentsController = function() { return self.scrollToComment(node, -1, true); }; - this.deleteComment = function(node) { - if (!confirm(_gettext('Delete this comment?'))) { - return false; - } + this._deleteComment = function(node) { var $node = $(node); var $td = $node.closest('td'); var $comment = $node.closest('.comment'); @@ -576,13 +572,33 @@ var CommentsController = function() { $comment.remove(); return false; }; - var failure = function(data, textStatus, xhr) { - alert("error processing request: " + textStatus); + var failure = function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while deleting this comment.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); + $comment.show('fast'); $comment.removeClass('comment-deleting'); return false; }; ajaxPOST(url, postData, success, failure); + } + + this.deleteComment = function(node) { + var $comment = $(node).closest('.comment'); + var comment_id = $comment.attr('data-comment-id'); + + SwalNoAnimation.fire({ + title: 'Delete this comment?', + icon: 'warning', + showCancelButton: true, + confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id), + + }).then(function(result) { + if (result.value) { + self._deleteComment(node); + } + }) }; this.toggleWideMode = function (node) { @@ -879,11 +895,10 @@ var CommentsController = function() { commentForm.setActionButtonsDisabled(false); }; - var submitFailCallback = function(data){ - alert( - "Error while submitting comment.\n" + - "Error code {0} ({1}).".format(data.status, data.statusText) - ); + var submitFailCallback = function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while submitting comment.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); commentForm.resetCommentFormState(text) }; commentForm.submitAjaxPOST( diff --git a/rhodecode/public/js/src/rhodecode/constants.js b/rhodecode/public/js/src/rhodecode/constants.js --- a/rhodecode/public/js/src/rhodecode/constants.js +++ b/rhodecode/public/js/src/rhodecode/constants.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/files.js b/rhodecode/public/js/src/rhodecode/files.js --- a/rhodecode/public/js/src/rhodecode/files.js +++ b/rhodecode/public/js/src/rhodecode/files.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 @@ -372,9 +372,13 @@ var getFilesMetadata = function() { var url_data = { 'repo_name': templateContext.repo_name, 'commit_id': state.commit_id, - 'f_path': state.f_path + 'f_path': state.f_path, }; + if (atRef !== '') { + url_data['at'] = atRef + } + var url = pyroutes.url('repo_nodetree_full', url_data); metadataRequest = $.ajax({url: url}); @@ -517,3 +521,80 @@ var showAuthors = function(elem, annotat return FileEditor; }); + +var checkFileHead = function($editForm, commit_id, f_path, operation) { + function getFormData($form){ + var unindexed_array = $form.serializeArray(); + var indexed_array = {}; + + $.map(unindexed_array, function(n, i){ + indexed_array[n['name']] = n['value']; + }); + + return indexed_array; + } + + $editForm.on('submit', function (e) { + + var validHead = $editForm.find('#commit_btn').data('validHead'); + if (validHead === true){ + return true + } + + // no marker, we do "checks" + e.preventDefault(); + var formData = getFormData($editForm); + var new_path = formData['filename']; + + var success = function(data) { + + if (data['is_head'] === true && data['path_exists'] === "") { + + $editForm.find('#commit_btn').data('validHead', true); + $editForm.find('#commit_btn').val('Committing...'); + $editForm.submit(); + + } else { + var message = ''; + var urlTmpl = 'here'; + $editForm.find('#commit_btn').val('Commit aborted'); + + if (operation === 'edit') { + var new_url = urlTmpl.format(pyroutes.url('repo_files_edit_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path})); + 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)); + } else if (operation === 'delete') { + var new_url = urlTmpl.format(pyroutes.url('repo_files_remove_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path})); + 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)); + } else if (operation === 'create') { + if (data['path_exists'] !== "") { + message = _gettext('There is an existing path `{0}` at this commit.'.format(data['path_exists'])); + } else { + 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})); + message = _gettext('There is a later version of file tree available. Click {0} to create a file at the latest tree.'.format(new_url)); + } + } + + var payload = { + message: { + message: message, + level: 'warning', + force: true + } + }; + $.Topic('/notifications').publish(payload); + } + }; + + // lock button + $editForm.find('#commit_btn').attr('disabled', 'disabled'); + $editForm.find('#commit_btn').val('Checking commit...'); + + var postData = {'csrf_token': CSRF_TOKEN, 'path': new_path, 'operation': operation}; + ajaxPOST(pyroutes.url('repo_files_check_head', + {'repo_name': templateContext.repo_name, 'commit_id': commit_id, 'f_path': f_path}), + postData, success); + + return false + + }); +}; diff --git a/rhodecode/public/js/src/rhodecode/followers.js b/rhodecode/public/js/src/rhodecode/followers.js --- a/rhodecode/public/js/src/rhodecode/followers.js +++ b/rhodecode/public/js/src/rhodecode/followers.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/init.js b/rhodecode/public/js/src/rhodecode/init.js --- a/rhodecode/public/js/src/rhodecode/init.js +++ b/rhodecode/public/js/src/rhodecode/init.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/menus.js b/rhodecode/public/js/src/rhodecode/menus.js --- a/rhodecode/public/js/src/rhodecode/menus.js +++ b/rhodecode/public/js/src/rhodecode/menus.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/notifications.js b/rhodecode/public/js/src/rhodecode/notifications.js --- a/rhodecode/public/js/src/rhodecode/notifications.js +++ b/rhodecode/public/js/src/rhodecode/notifications.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/permissions.js b/rhodecode/public/js/src/rhodecode/permissions.js --- a/rhodecode/public/js/src/rhodecode/permissions.js +++ b/rhodecode/public/js/src/rhodecode/permissions.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/pjax.js b/rhodecode/public/js/src/rhodecode/pjax.js --- a/rhodecode/public/js/src/rhodecode/pjax.js +++ b/rhodecode/public/js/src/rhodecode/pjax.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/pullrequests.js b/rhodecode/public/js/src/rhodecode/pullrequests.js --- a/rhodecode/public/js/src/rhodecode/pullrequests.js +++ b/rhodecode/public/js/src/rhodecode/pullrequests.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 @@ -75,12 +75,12 @@ var getTitleAndDescription = function(so var desc = ''; $.each($(elements).get().reverse().slice(0, limit), function(idx, value) { - var rawMessage = $(value).find('td.td-description .message').data('messageRaw').toString(); + var rawMessage = value['message']; desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n'; }); // only 1 commit, use commit message as title if (elements.length === 1) { - var rawMessage = $(elements[0]).find('td.td-description .message').data('messageRaw').toString(); + var rawMessage = elements[0]['message']; title = rawMessage.split('\n')[0]; } else { @@ -92,7 +92,6 @@ var getTitleAndDescription = function(so }; - ReviewersController = function () { var self = this; this.$reviewRulesContainer = $('#review_rules'); @@ -100,28 +99,35 @@ ReviewersController = function () { this.forbidReviewUsers = undefined; this.$reviewMembers = $('#review_members'); this.currentRequest = null; + this.diffData = null; + //dummy handler, we might register our own later + this.diffDataHandler = function(data){}; - this.defaultForbidReviewUsers = function() { + this.defaultForbidReviewUsers = function () { return [ - {'username': 'default', - 'user_id': templateContext.default_user.user_id} + { + 'username': 'default', + 'user_id': templateContext.default_user.user_id + } ]; }; - this.hideReviewRules = function() { + this.hideReviewRules = function () { self.$reviewRulesContainer.hide(); }; - this.showReviewRules = function() { + this.showReviewRules = function () { self.$reviewRulesContainer.show(); }; - this.addRule = function(ruleText) { + this.addRule = function (ruleText) { self.showReviewRules(); return '
- {0}
'.format(ruleText) }; - this.loadReviewRules = function(data) { + this.loadReviewRules = function (data) { + self.diffData = data; + // reset forbidden Users this.forbidReviewUsers = self.defaultForbidReviewUsers(); @@ -141,7 +147,7 @@ ReviewersController = function () { if (data.rules.voting < 0) { self.$rulesList.append( self.addRule( - _gettext('All individual reviewers must vote.')) + _gettext('All individual reviewers must vote.')) ) } else if (data.rules.voting === 1) { self.$rulesList.append( @@ -158,7 +164,7 @@ ReviewersController = function () { } if (data.rules.voting_groups !== undefined) { - $.each(data.rules.voting_groups, function(index, rule_data) { + $.each(data.rules.voting_groups, function (index, rule_data) { self.$rulesList.append( self.addRule(rule_data.text) ) @@ -188,7 +194,7 @@ ReviewersController = function () { if (data.rules.forbid_commit_author_to_review) { if (data.rules_data.forbidden_users) { - $.each(data.rules_data.forbidden_users, function(index, member_data) { + $.each(data.rules_data.forbidden_users, function (index, member_data) { self.forbidReviewUsers.push(member_data) }); @@ -203,11 +209,10 @@ ReviewersController = function () { return self.forbidReviewUsers }; - this.loadDefaultReviewers = function(sourceRepo, sourceRef, targetRepo, targetRef) { + this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) { if (self.currentRequest) { - // make sure we cleanup old running requests before triggering this - // again + // make sure we cleanup old running requests before triggering this again self.currentRequest.abort(); } @@ -218,6 +223,9 @@ ReviewersController = function () { prButtonLock(true, null, 'reviewers'); $('#user').hide(); // hide user autocomplete before load + // lock PR button, so we cannot send PR before it's calculated + prButtonLock(true, _gettext('Loading diff ...'), 'compare'); + if (sourceRef.length !== 3 || targetRef.length !== 3) { // don't load defaults in case we're missing some refs... $('.calculate-reviewers').hide(); @@ -225,58 +233,80 @@ ReviewersController = function () { } var url = pyroutes.url('repo_default_reviewers_data', - { - 'repo_name': templateContext.repo_name, - 'source_repo': sourceRepo, - 'source_ref': sourceRef[2], - 'target_repo': targetRepo, - 'target_ref': targetRef[2] - }); + { + 'repo_name': templateContext.repo_name, + 'source_repo': sourceRepo, + 'source_ref': sourceRef[2], + 'target_repo': targetRepo, + 'target_ref': targetRef[2] + }); - self.currentRequest = $.get(url) - .done(function(data) { + self.currentRequest = $.ajax({ + url: url, + headers: {'X-PARTIAL-XHR': true}, + type: 'GET', + success: function (data) { + self.currentRequest = null; // review rules self.loadReviewRules(data); + self.handleDiffData(data["diff_info"]); for (var i = 0; i < data.reviewers.length; i++) { - var reviewer = data.reviewers[i]; - self.addReviewMember( - reviewer, reviewer.reasons, reviewer.mandatory); + var reviewer = data.reviewers[i]; + self.addReviewMember(reviewer, reviewer.reasons, reviewer.mandatory); } $('.calculate-reviewers').hide(); prButtonLock(false, null, 'reviewers'); $('#user').show(); // show user autocomplete after load - }); + + var commitElements = data["diff_info"]['commits']; + if (commitElements.length === 0) { + prButtonLock(true, _gettext('no commits'), 'all'); + + } else { + // un-lock PR button, so we cannot send PR before it's calculated + prButtonLock(false, null, 'compare'); + } + + }, + error: function (jqXHR, textStatus, errorThrown) { + var prefix = "Loading diff and reviewers failed\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); + } + }); + }; // check those, refactor - this.removeReviewMember = function(reviewer_id, mark_delete) { + this.removeReviewMember = function (reviewer_id, mark_delete) { var reviewer = $('#reviewer_{0}'.format(reviewer_id)); - if(typeof(mark_delete) === undefined){ + if (typeof (mark_delete) === undefined) { mark_delete = false; } - if(mark_delete === true){ - if (reviewer){ + if (mark_delete === true) { + if (reviewer) { // now delete the input $('#reviewer_{0} input'.format(reviewer_id)).remove(); // mark as to-delete var obj = $('#reviewer_{0}_name'.format(reviewer_id)); obj.addClass('to-delete'); - obj.css({"text-decoration":"line-through", "opacity": 0.5}); + obj.css({"text-decoration": "line-through", "opacity": 0.5}); } - } - else{ + } else { $('#reviewer_{0}'.format(reviewer_id)).remove(); } }; - this.reviewMemberEntry = function() { + + this.reviewMemberEntry = function () { }; - this.addReviewMember = function(reviewer_obj, reasons, mandatory) { + + this.addReviewMember = function (reviewer_obj, reasons, mandatory) { var members = self.$reviewMembers.get(0); var id = reviewer_obj.user_id; var username = reviewer_obj.username; @@ -287,13 +317,13 @@ ReviewersController = function () { // register IDS to check if we don't have this ID already in var currentIds = []; var _els = self.$reviewMembers.find('li').toArray(); - for (el in _els){ + for (el in _els) { currentIds.push(_els[el].id) } - var userAllowedReview = function(userId) { + var userAllowedReview = function (userId) { var allowed = true; - $.each(self.forbidReviewUsers, function(index, member_data) { + $.each(self.forbidReviewUsers, function (index, member_data) { if (parseInt(userId) === member_data['user_id']) { allowed = false; return false // breaks the loop @@ -303,35 +333,38 @@ ReviewersController = function () { }; var userAllowed = userAllowedReview(id); - if (!userAllowed){ - alert(_gettext('User `{0}` not allowed to be a reviewer').format(username)); + if (!userAllowed) { + alert(_gettext('User `{0}` not allowed to be a reviewer').format(username)); } else { // only add if it's not there - var alreadyReviewer = currentIds.indexOf('reviewer_'+id) != -1; + var alreadyReviewer = currentIds.indexOf('reviewer_' + id) != -1; if (alreadyReviewer) { alert(_gettext('User `{0}` already in reviewers').format(username)); } else { members.innerHTML += renderTemplate('reviewMemberEntry', { - 'member': reviewer_obj, - 'mandatory': mandatory, - 'allowed_to_update': true, - 'review_status': 'not_reviewed', - 'review_status_label': _gettext('Not Reviewed'), - 'reasons': reasons, - 'create': true - }); + 'member': reviewer_obj, + 'mandatory': mandatory, + 'allowed_to_update': true, + 'review_status': 'not_reviewed', + 'review_status_label': _gettext('Not Reviewed'), + 'reasons': reasons, + 'create': true + }); tooltipActivate(); } } }; - this.updateReviewers = function(repo_name, pull_request_id){ + this.updateReviewers = function (repo_name, pull_request_id) { var postData = $('#reviewers input').serialize(); _updatePullRequest(repo_name, pull_request_id, postData); }; + this.handleDiffData = function (data) { + self.diffDataHandler(data) + } }; diff --git a/rhodecode/public/js/src/rhodecode/settings.js b/rhodecode/public/js/src/rhodecode/settings.js --- a/rhodecode/public/js/src/rhodecode/settings.js +++ b/rhodecode/public/js/src/rhodecode/settings.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/users.js b/rhodecode/public/js/src/rhodecode/users.js --- a/rhodecode/public/js/src/rhodecode/users.js +++ b/rhodecode/public/js/src/rhodecode/users.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 @@ -47,3 +47,51 @@ var UsersAutoComplete = function(input_i lookupFilter: autocompleteFilterResult }); }; + +var _showAuthToken = function (authTokenId, showUrl) { + + SwalNoAnimation.fire({ + title: _gettext('Show this authentication token?'), + showCancelButton: true, + confirmButtonText: _gettext('Show'), + showLoaderOnConfirm: true, + allowOutsideClick: function () { + !Swal.isLoading() + }, + preConfirm: function () { + + var postData = { + 'auth_token_id': authTokenId, + 'csrf_token': CSRF_TOKEN + }; + return new Promise(function (resolve, reject) { + $.ajax({ + type: 'POST', + data: postData, + url: showUrl, + headers: {'X-PARTIAL-XHR': true} + }) + .done(function (data) { + resolve(data); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + //reject("Failed to fetch Authentication Token") + var message = formatErrorMessage(jqXHR, textStatus, errorThrown); + ajaxErrorSwal(message); + }); + }) + } + + }) + .then(function (result) { + if (result.value) { + var tmpl = ('{0}' + + ''); + + SwalNoAnimation.fire({ + title: _gettext('Authentication Token'), + html: tmpl.format(result.value.auth_token, result.value.auth_token), + }) + } + }) +} \ No newline at end of file diff --git a/rhodecode/public/js/src/rhodecode/utils/ajax.js b/rhodecode/public/js/src/rhodecode/utils/ajax.js --- a/rhodecode/public/js/src/rhodecode/utils/ajax.js +++ b/rhodecode/public/js/src/rhodecode/utils/ajax.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 @@ -36,37 +36,140 @@ var toQueryString = function(o) { /** * ajax call wrappers */ -var ajaxGET = function(url, success, failure) { - var sUrl = url; - var request = $.ajax({url: sUrl, headers: {'X-PARTIAL-XHR': true}}) - .done(function(data){ - success(data); - }) - .fail(function(data, textStatus, xhr) { - if (failure) { - failure(data, textStatus, xhr); - } else { - alert("Error processing request. \n" + - "Error code {0} ({1}).".format(data.status, data.statusText)); - } - }); - return request; + +var ajaxGET = function (url, success, failure) { + var sUrl = url; + var request = $.ajax({ + url: sUrl, + headers: {'X-PARTIAL-XHR': true} + }) + .done(function (data) { + success(data); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + if (failure) { + failure(jqXHR, textStatus, errorThrown); + } else { + var message = formatErrorMessage(jqXHR, textStatus, errorThrown); + ajaxErrorSwal(message); + } + }); + return request; +}; + +var ajaxPOST = function (url, postData, success, failure) { + var sUrl = url; + var postData = toQueryString(postData); + var request = $.ajax({ + type: 'POST', + url: sUrl, + data: postData, + headers: {'X-PARTIAL-XHR': true} + }) + .done(function (data) { + success(data); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + if (failure) { + failure(jqXHR, textStatus, errorThrown); + } else { + var message = formatErrorMessage(jqXHR, textStatus, errorThrown); + ajaxErrorSwal(message); + } + }); + return request; }; -var ajaxPOST = function(url, postData, success, failure) { - var sUrl = url; - var postData = toQueryString(postData); - var request = $.ajax({type: 'POST', data: postData, url: sUrl, - headers: {'X-PARTIAL-XHR': true}}) - .done(function(data){ - success(data); - }) - .fail(function(data, textStatus, xhr){ - if (failure) { - failure(data, textStatus, xhr); + + +SwalNoAnimation = Swal.mixin({ + confirmButtonColor: '#84a5d2', + cancelButtonColor: '#e85e4d', + showClass: { + popup: 'swal2-noanimation', + backdrop: 'swal2-noanimation' + }, + hideClass: { + popup: '', + backdrop: '' + }, +}) + + +/* Example usage: +* + error: function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while fetching entries.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); + } +* +* */ +function formatErrorMessage(jqXHR, textStatus, errorThrown, prefix) { + if(typeof prefix === "undefined") { + prefix = '' + } + + if (jqXHR.status === 0) { + return (prefix + 'Not connected.\nPlease verify your network connection.'); + } else if (jqXHR.status == 401) { + return (prefix + 'Unauthorized access. [401]'); + } else if (jqXHR.status == 404) { + return (prefix + 'The requested page not found. [404]'); + } else if (jqXHR.status == 500) { + return (prefix + 'Internal Server Error [500].'); + } else if (jqXHR.status == 503) { + return (prefix + 'Service unavailable [503].'); + } else if (errorThrown === 'parsererror') { + return (prefix + 'Requested JSON parse failed.'); + } else if (errorThrown === 'timeout') { + return (prefix + 'Time out error.'); + } else if (errorThrown === 'abort') { + return (prefix + 'Ajax request aborted.'); } else { - alert("Error processing request. \n" + - "Error code {0} ({1}).".format(data.status, data.statusText)); + return (prefix + 'Uncaught Error.\n' + jqXHR.responseText); } - }); - return request; -}; +} + +function ajaxErrorSwal(message) { + SwalNoAnimation.fire({ + icon: 'error', + title: _gettext('Ajax Request Error'), + html: '{0}'.format(message), + showClass: { + popup: 'swal2-noanimation', + backdrop: 'swal2-noanimation' + }, + hideClass: { + popup: '', + backdrop: '' + } + }) +} + +/* +* use in onclick attributes e.g +* onclick="submitConfirm(event, this, _gettext('Confirm to delete '), _gettext('Confirm Delete'), 'what we delete')"> +* */ +function submitConfirm(event, self, question, confirmText, htmlText) { + if (htmlText === "undefined") { + htmlText = null; + } + if (confirmText === "undefined") { + confirmText = _gettext('Delete') + } + event.preventDefault(); + + SwalNoAnimation.fire({ + title: question, + icon: 'warning', + html: htmlText, + + showCancelButton: true, + + confirmButtonText: confirmText + }).then(function(result) { + if (result.value) { + $(self).closest("form").submit(); + } + }) +} \ No newline at end of file diff --git a/rhodecode/public/js/src/rhodecode/utils/array.js b/rhodecode/public/js/src/rhodecode/utils/array.js --- a/rhodecode/public/js/src/rhodecode/utils/array.js +++ b/rhodecode/public/js/src/rhodecode/utils/array.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/autocomplete.js b/rhodecode/public/js/src/rhodecode/utils/autocomplete.js --- a/rhodecode/public/js/src/rhodecode/utils/autocomplete.js +++ b/rhodecode/public/js/src/rhodecode/utils/autocomplete.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/colorgenerator.js b/rhodecode/public/js/src/rhodecode/utils/colorgenerator.js --- a/rhodecode/public/js/src/rhodecode/utils/colorgenerator.js +++ b/rhodecode/public/js/src/rhodecode/utils/colorgenerator.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/ie.js b/rhodecode/public/js/src/rhodecode/utils/ie.js --- a/rhodecode/public/js/src/rhodecode/utils/ie.js +++ b/rhodecode/public/js/src/rhodecode/utils/ie.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/os.js b/rhodecode/public/js/src/rhodecode/utils/os.js --- a/rhodecode/public/js/src/rhodecode/utils/os.js +++ b/rhodecode/public/js/src/rhodecode/utils/os.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/pyroutes.js b/rhodecode/public/js/src/rhodecode/utils/pyroutes.js --- a/rhodecode/public/js/src/rhodecode/utils/pyroutes.js +++ b/rhodecode/public/js/src/rhodecode/utils/pyroutes.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/string.js b/rhodecode/public/js/src/rhodecode/utils/string.js --- a/rhodecode/public/js/src/rhodecode/utils/string.js +++ b/rhodecode/public/js/src/rhodecode/utils/string.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/rhodecode/utils/topics.js b/rhodecode/public/js/src/rhodecode/utils/topics.js --- a/rhodecode/public/js/src/rhodecode/utils/topics.js +++ b/rhodecode/public/js/src/rhodecode/utils/topics.js @@ -1,4 +1,4 @@ -// # Copyright (C) 2010-2019 RhodeCode GmbH +// # Copyright (C) 2010-2020 RhodeCode GmbH // # // # This program is free software: you can redistribute it and/or modify // # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/public/js/src/select2/select2.css b/rhodecode/public/js/src/select2/select2.css --- a/rhodecode/public/js/src/select2/select2.css +++ b/rhodecode/public/js/src/select2/select2.css @@ -393,6 +393,7 @@ html[dir="rtl"] .select2-results { -moz-user-select: none; -ms-user-select: none; user-select: none; + white-space: nowrap; } .select2-results-dept-1 .select2-result-label { padding-left: 20px } diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/templates/admin/gists/gist_show.mako b/rhodecode/templates/admin/gists/gist_show.mako --- a/rhodecode/templates/admin/gists/gist_show.mako +++ b/rhodecode/templates/admin/gists/gist_show.mako @@ -54,7 +54,10 @@ %if c.is_super_admin or c.gist.gist_owner == c.rhodecode_user.user_id:
${h.secure_form(h.route_path('gist_delete', gist_id=c.gist.gist_access_id), request=request)} - ${h.submit('remove_gist', _('Delete'),class_="btn btn-mini btn-danger",onclick="return confirm('"+_('Confirm to delete this Gist')+"');")} + ${h.end_form()}
%endif diff --git a/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako b/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako --- a/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako +++ b/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako @@ -1,4 +1,10 @@
+ +

${_('Authentication Tokens')}

@@ -21,9 +27,11 @@ %if c.user_auth_tokens: %for auth_token in c.user_auth_tokens: - -
- ${auth_token.api_key} + +
+ + ${auth_token.token_obfuscated} +
${auth_token.description} @@ -44,9 +52,10 @@ ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)} - ${h.hidden('del_auth_token', auth_token.user_api_key_id)} + ${h.end_form()} diff --git a/rhodecode/templates/admin/my_account/my_account_emails.mako b/rhodecode/templates/admin/my_account/my_account_emails.mako --- a/rhodecode/templates/admin/my_account/my_account_emails.mako +++ b/rhodecode/templates/admin/my_account/my_account_emails.mako @@ -28,7 +28,8 @@ ${h.secure_form(h.route_path('my_account_emails_delete'), request=request)} ${h.hidden('del_email_id',em.email_id)} ${h.end_form()} diff --git a/rhodecode/templates/admin/my_account/my_account_pullrequests.mako b/rhodecode/templates/admin/my_account/my_account_pullrequests.mako --- a/rhodecode/templates/admin/my_account/my_account_pullrequests.mako +++ b/rhodecode/templates/admin/my_account/my_account_pullrequests.mako @@ -2,11 +2,29 @@
- %if c.closed: - ${h.checkbox('show_closed',checked="checked", label=_('Show Closed Pull Requests'))} - %else: - ${h.checkbox('show_closed',label=_('Show Closed Pull Requests'))} - %endif +
+ <% + selected_filter = 'all' + if c.closed: + selected_filter = 'all_closed' + %> + + + +
+
    +
  • + +
  • +
  • + +
  • +
+
+
@@ -20,72 +38,107 @@
diff --git a/rhodecode/templates/admin/my_account/my_account_ssh_keys.mako b/rhodecode/templates/admin/my_account/my_account_ssh_keys.mako --- a/rhodecode/templates/admin/my_account/my_account_ssh_keys.mako +++ b/rhodecode/templates/admin/my_account/my_account_ssh_keys.mako @@ -29,7 +29,8 @@ ${h.secure_form(h.route_path('my_account_ssh_keys_delete'), request=request)} ${h.hidden('del_ssh_key', ssh_key.ssh_key_id)} ${h.end_form()} diff --git a/rhodecode/templates/admin/repos/repo_add_base.mako b/rhodecode/templates/admin/repos/repo_add_base.mako --- a/rhodecode/templates/admin/repos/repo_add_base.mako +++ b/rhodecode/templates/admin/repos/repo_add_base.mako @@ -52,12 +52,29 @@ ${_('Optionally select a group to put this repository into.')}
+
-
- ${h.select('repo_type','hg',c.backends)} +
+ + + % for backend in c.backends: + % if loop.index == 0: + + % else: + + % endif + + + + % endfor + + ${_('Set the type of repository to create.')}
@@ -140,12 +157,6 @@ setCopyPermsOption(e.val) }); - $("#repo_type").select2({ - 'containerCssClass': "drop-menu", - 'dropdownCssClass': "drop-menu-dropdown", - 'minimumResultsForSearch': -1, - }); - $('#repo_name').focus(); $('#select_my_group').on('click', function(e){ diff --git a/rhodecode/templates/admin/repos/repo_edit_advanced.mako b/rhodecode/templates/admin/repos/repo_edit_advanced.mako --- a/rhodecode/templates/admin/repos/repo_edit_advanced.mako +++ b/rhodecode/templates/admin/repos/repo_edit_advanced.mako @@ -93,14 +93,16 @@ %if c.rhodecode_db_repo.locked[0]: ${h.hidden('set_unlock', '1')} %else: ${h.hidden('set_lock', '1')} @@ -147,6 +149,11 @@ onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');"> ${_('Update Hooks')} + % if c.hooks_outdated: + + ${_('Outdated hooks detected, please update hooks using `Update Hooks` action.')} + + % endif
@@ -161,7 +168,8 @@
@@ -216,7 +224,8 @@
diff --git a/rhodecode/templates/admin/repos/repo_edit_caches.mako b/rhodecode/templates/admin/repos/repo_edit_caches.mako --- a/rhodecode/templates/admin/repos/repo_edit_caches.mako +++ b/rhodecode/templates/admin/repos/repo_edit_caches.mako @@ -17,7 +17,11 @@ ${h.secure_form(h.route_path('edit_repo_caches', repo_name=c.repo_name), request=request)}
- ${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')+"');")} +
${h.end_form()} diff --git a/rhodecode/templates/admin/repos/repo_edit_permissions.mako b/rhodecode/templates/admin/repos/repo_edit_permissions.mako --- a/rhodecode/templates/admin/repos/repo_edit_permissions.mako +++ b/rhodecode/templates/admin/repos/repo_edit_permissions.mako @@ -59,7 +59,7 @@ ${base.gravatar(h.DEFAULT_USER_EMAIL, 16)} ${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')} - + ${_('un-set private mode')} @@ -115,7 +115,7 @@ ${_('Remove')} %elif _user.username == h.DEFAULT_USER: - + ${_('set private mode')} %endif @@ -213,7 +213,14 @@ }); quick_repo_menu(); - var setPrivateRepo = function (private) { + var setPrivateRepo = function (elem, private) { + var $elem = $(elem) + if ($elem.hasClass('disabled')) { + return + } + $elem.addClass('disabled'); + $elem.css({"opacity": 0.3}) + var postData = { 'csrf_token': CSRF_TOKEN, 'private': private diff --git a/rhodecode/templates/admin/repos/repo_edit_remote.mako b/rhodecode/templates/admin/repos/repo_edit_remote.mako --- a/rhodecode/templates/admin/repos/repo_edit_remote.mako +++ b/rhodecode/templates/admin/repos/repo_edit_remote.mako @@ -37,7 +37,12 @@ ${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), request=request)}
- ${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')+"');")} +
${h.end_form()} diff --git a/rhodecode/templates/admin/settings/settings_exceptions.mako b/rhodecode/templates/admin/settings/settings_exceptions.mako --- a/rhodecode/templates/admin/settings/settings_exceptions.mako +++ b/rhodecode/templates/admin/settings/settings_exceptions.mako @@ -6,8 +6,17 @@ % if c.traceback:

${_('Exception `{}` generated on UTC date: {}').format(c.traceback.get('exc_type', 'NO_TYPE'), c.traceback.get('exc_utc_date', 'NO_DATE'))}

+ % if c.traceback.get('url'): + Request: + ${c.traceback.get('method')} ${c.traceback.get('url')}
+ ${c.traceback.get('client_address')} ${c.traceback.get('user_agent')} +
+
+ % endif +
${c.traceback.get('exc_message', 'NO_MESSAGE')}
+ % else: ${_('Unable to Read Exception. It might be removed or non-existing.')} % endif @@ -26,7 +35,7 @@
diff --git a/rhodecode/templates/admin/settings/settings_exceptions_browse.mako b/rhodecode/templates/admin/settings/settings_exceptions_browse.mako --- a/rhodecode/templates/admin/settings/settings_exceptions_browse.mako +++ b/rhodecode/templates/admin/settings/settings_exceptions_browse.mako @@ -16,7 +16,8 @@
-
- % for dt, dd in c.py_modules['human_value']: -
${dt}${':' if dt else '---'}
-
${dd}${'' if dt else '---'}
- % endfor -
+ + + + + % for name, package_data in c.py_modules['human_value']: + + + + + + % endfor +
${name.lower()}${package_data['version']}(${package_data['location']})
+
diff --git a/rhodecode/templates/admin/settings/settings_system_snapshot.mako b/rhodecode/templates/admin/settings/settings_system_snapshot.mako --- a/rhodecode/templates/admin/settings/settings_system_snapshot.mako +++ b/rhodecode/templates/admin/settings/settings_system_snapshot.mako @@ -40,8 +40,8 @@ SYSTEM SETTINGS PYTHON PACKAGES --------------- -% for key, value in c.py_modules['human_value']: -${'{:<60}'.format(key)}: ${value} +% for name, package_data in c.py_modules['human_value']: +${'{:<60}'.format(name)}: ${'{:<20}'.format(package_data['version'])} (${package_data['location']}) % endfor diff --git a/rhodecode/templates/admin/user_groups/user_group_edit_settings.mako b/rhodecode/templates/admin/user_groups/user_group_edit_settings.mako --- a/rhodecode/templates/admin/user_groups/user_group_edit_settings.mako +++ b/rhodecode/templates/admin/user_groups/user_group_edit_settings.mako @@ -181,6 +181,7 @@ } else if (suggestion.value_type == 'user') { addMember(suggestion, null); } + $('#user_group_add_members').val('') } }); diff --git a/rhodecode/templates/admin/users/user_edit_advanced.mako b/rhodecode/templates/admin/users/user_edit_advanced.mako --- a/rhodecode/templates/admin/users/user_edit_advanced.mako +++ b/rhodecode/templates/admin/users/user_edit_advanced.mako @@ -149,6 +149,18 @@ + ${_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)} + + + + + + + + + + + ${_ungettext('This user owns %s artifact.', 'This user owns %s artifacts.', len(c.user.artifacts)) % len(c.user.artifacts)} @@ -166,7 +178,8 @@ % endif ${_('New owner for detached objects')}: -
${base.gravatar_with_user(c.first_admin.email, 16)}
+
${base.gravatar_with_user(c.detach_user.email, 16, tooltip=True)}
+
@@ -186,11 +199,11 @@
- +
${h.end_form()} diff --git a/rhodecode/templates/admin/users/user_edit_auth_tokens.mako b/rhodecode/templates/admin/users/user_edit_auth_tokens.mako --- a/rhodecode/templates/admin/users/user_edit_auth_tokens.mako +++ b/rhodecode/templates/admin/users/user_edit_auth_tokens.mako @@ -1,6 +1,12 @@ <%namespace name="base" file="/base/base.mako"/>
+ +

${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')} @@ -26,9 +32,11 @@ %if c.user_auth_tokens: %for auth_token in c.user_auth_tokens: - -
- ${auth_token.api_key} + +
+ + ${auth_token.token_obfuscated} +
${auth_token.description} @@ -49,7 +57,7 @@ ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), request=request)} - ${h.hidden('del_auth_token', auth_token.user_api_key_id)} +
${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)} - ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger", - 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)+"');")} + ${h.end_form()}
@@ -265,8 +269,10 @@

${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)} - ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger", - onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")} + ${h.end_form()}
@@ -277,8 +283,10 @@
${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)} - ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger", - onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")} + ${h.end_form()}
@@ -423,8 +431,10 @@ % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)} - ${h.submit('remove_',_('Delete'),id="remove_artifact_%s" % file_store_id, class_="btn btn-link btn-danger", - onclick="return confirm('"+_('Confirm to delete this artifact: %s') % file_uid+"');")} + ${h.end_form()}
% endif diff --git a/rhodecode/templates/email_templates/base.mako b/rhodecode/templates/email_templates/base.mako --- a/rhodecode/templates/email_templates/base.mako +++ b/rhodecode/templates/email_templates/base.mako @@ -484,21 +484,84 @@ text_monospace = "'Menlo', 'Liberation M background-color: #F5F5F5 } + /*elasticmatch is custom rhodecode tag*/ + .codehilite .c-ElasticMatch { + background-color: #faffa6; + padding: 0.2em; + } + + .codehilite .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;} + .codehilite .hll { background-color: #ffffcc } + .codehilite .c { color: #408080; font-style: italic } /* Comment */ + .codehilite .err { border: none } /* Error */ + .codehilite .k { color: #008000; font-weight: bold } /* Keyword */ + .codehilite .o { color: #666666 } /* Operator */ + .codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */ + .codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */ + .codehilite .cp { color: #BC7A00 } /* Comment.Preproc */ + .codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */ + .codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */ + .codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */ + .codehilite .gd { color: #A00000 } /* Generic.Deleted */ + .codehilite .ge { font-style: italic } /* Generic.Emph */ + .codehilite .gr { color: #FF0000 } /* Generic.Error */ + .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ + .codehilite .gi { color: #00A000 } /* Generic.Inserted */ + .codehilite .go { color: #888888 } /* Generic.Output */ + .codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ + .codehilite .gs { font-weight: bold } /* Generic.Strong */ + .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ + .codehilite .gt { color: #0044DD } /* Generic.Traceback */ + .codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ + .codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ + .codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ + .codehilite .kp { color: #008000 } /* Keyword.Pseudo */ + .codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ + .codehilite .kt { color: #B00040 } /* Keyword.Type */ + .codehilite .m { color: #666666 } /* Literal.Number */ + .codehilite .s { color: #BA2121 } /* Literal.String */ + .codehilite .na { color: #7D9029 } /* Name.Attribute */ + .codehilite .nb { color: #008000 } /* Name.Builtin */ + .codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ + .codehilite .no { color: #880000 } /* Name.Constant */ + .codehilite .nd { color: #AA22FF } /* Name.Decorator */ + .codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */ + .codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ + .codehilite .nf { color: #0000FF } /* Name.Function */ + .codehilite .nl { color: #A0A000 } /* Name.Label */ + .codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ + .codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ + .codehilite .nv { color: #19177C } /* Name.Variable */ + .codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ + .codehilite .w { color: #bbbbbb } /* Text.Whitespace */ + .codehilite .mb { color: #666666 } /* Literal.Number.Bin */ + .codehilite .mf { color: #666666 } /* Literal.Number.Float */ + .codehilite .mh { color: #666666 } /* Literal.Number.Hex */ + .codehilite .mi { color: #666666 } /* Literal.Number.Integer */ + .codehilite .mo { color: #666666 } /* Literal.Number.Oct */ + .codehilite .sa { color: #BA2121 } /* Literal.String.Affix */ + .codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ + .codehilite .sc { color: #BA2121 } /* Literal.String.Char */ + .codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */ + .codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ + .codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ + .codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ + .codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ + .codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ + .codehilite .sx { color: #008000 } /* Literal.String.Other */ + .codehilite .sr { color: #BB6688 } /* Literal.String.Regex */ + .codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ + .codehilite .ss { color: #19177C } /* Literal.String.Symbol */ + .codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ + .codehilite .fm { color: #0000FF } /* Name.Function.Magic */ + .codehilite .vc { color: #19177C } /* Name.Variable.Class */ + .codehilite .vg { color: #19177C } /* Name.Variable.Global */ + .codehilite .vi { color: #19177C } /* Name.Variable.Instance */ + .codehilite .vm { color: #19177C } /* Name.Variable.Magic */ + .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ - - - - diff --git a/rhodecode/templates/email_templates/exception_tracker.mako b/rhodecode/templates/email_templates/exception_tracker.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/email_templates/exception_tracker.mako @@ -0,0 +1,18 @@ +## -*- coding: utf-8 -*- +<%inherit file="base.mako"/> +<%namespace name="base" file="base.mako"/> + +<%def name="subject()" filter="n,trim,whitespace_filter"> +${email_prefix} ${exc_type_name} (${exc_id}) + + +## plain text version of the email. Empty by default +<%def name="body_plaintext()" filter="n,trim"> + NO PLAINTEXT VERSION + + +

${_('Exception `{}` generated on UTC date: {}').format(exc_traceback.get('exc_type', 'NO_TYPE'), exc_traceback.get('exc_utc_date', 'NO_DATE'))}

+

+ View exception ${exc_id} +

+
${exc_traceback.get('exc_message', 'NO_MESSAGE')}
diff --git a/rhodecode/templates/errors/error_document.mako b/rhodecode/templates/errors/error_document.mako --- a/rhodecode/templates/errors/error_document.mako +++ b/rhodecode/templates/errors/error_document.mako @@ -21,6 +21,7 @@ // register templateContext to pass template variables to JS var templateContext = {timeago: {}}; + <%include file="/base/plugins_base.mako"/> diff --git a/rhodecode/templates/files/base.mako b/rhodecode/templates/files/base.mako --- a/rhodecode/templates/files/base.mako +++ b/rhodecode/templates/files/base.mako @@ -1,10 +1,25 @@ -<%def name="refs(commit)"> - ## Build a cache of refs for selector +<%def name="refs(commit, at_rev=None)"> + + ## Build a cache of refs for selector, based on this the files ref selector gets pre-selected values - } - + % if h.is_svn(c.rhodecode_repo): + ## since SVN doesn't have an commit<->refs association, we simply inject it + ## based on our at_rev marker + % if at_rev and at_rev.startswith('branches/'): + <% + commit.branch = at_rev + %> + % endif + % if at_rev and at_rev.startswith('tags/'): + <% + commit.tags.append(at_rev) + %> + % endif + + % endif %if commit.merge: diff --git a/rhodecode/templates/files/files.mako b/rhodecode/templates/files/files.mako --- a/rhodecode/templates/files/files.mako +++ b/rhodecode/templates/files/files.mako @@ -158,18 +158,24 @@ select2FileHistorySwitcher('#file_refs_filter', loadUrl, initialCommitData); + // switcher for files $('#file_refs_filter').on('change', function(e) { var data = $('#file_refs_filter').select2('data'); var commit_id = data.id; + var params = { + 'repo_name': templateContext.repo_name, + 'commit_id': commit_id, + 'f_path': state.f_path + }; + + if(data.at_rev !== undefined && data.at_rev !== "") { + params['at'] = data.at_rev; + } if ("${c.annotate}" === "True") { - var url = pyroutes.url('repo_files:annotated', - {'repo_name': templateContext.repo_name, - 'commit_id': commit_id, 'f_path': state.f_path}); + var url = pyroutes.url('repo_files:annotated', params); } else { - var url = pyroutes.url('repo_files', - {'repo_name': templateContext.repo_name, - 'commit_id': commit_id, 'f_path': state.f_path}); + var url = pyroutes.url('repo_files', params); } window.location = url; @@ -334,6 +340,7 @@ select2RefFileSwitcher('#refs_filter', loadUrl, initialCommitData); + // switcher for file tree $('#refs_filter').on('change', function(e) { var data = $('#refs_filter').select2('data'); window.location = data.files_url diff --git a/rhodecode/templates/files/files_add.mako b/rhodecode/templates/files/files_add.mako --- a/rhodecode/templates/files/files_add.mako +++ b/rhodecode/templates/files/files_add.mako @@ -41,7 +41,7 @@
  • - +
  • @@ -50,7 +50,6 @@
    -
    @@ -108,6 +107,10 @@ $('#filename').focus(); + var commit_id = "${c.commit.raw_id}"; + var f_path = "${c.f_path}"; + + checkFileHead($('#eform'), commit_id, f_path, 'create') }); diff --git a/rhodecode/templates/files/files_browser_tree.mako b/rhodecode/templates/files/files_browser_tree.mako --- a/rhodecode/templates/files/files_browser_tree.mako +++ b/rhodecode/templates/files/files_browser_tree.mako @@ -41,7 +41,6 @@ % endif % else: - ${node.name} diff --git a/rhodecode/templates/files/files_delete.mako b/rhodecode/templates/files/files_delete.mako --- a/rhodecode/templates/files/files_delete.mako +++ b/rhodecode/templates/files/files_delete.mako @@ -69,7 +69,7 @@
    - ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-danger-action")} + ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-danger-action")}
    ${h.end_form()} @@ -82,6 +82,10 @@ fileEditor = new FileEditor('#editor'); + var commit_id = "${c.commit.raw_id}"; + var f_path = "${c.f_path}"; + + checkFileHead($('#eform'), commit_id, f_path, 'delete'); }); diff --git a/rhodecode/templates/files/files_edit.mako b/rhodecode/templates/files/files_edit.mako --- a/rhodecode/templates/files/files_edit.mako +++ b/rhodecode/templates/files/files_edit.mako @@ -117,6 +117,11 @@ // on entering the new filename set mode, from given extension setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null); + var commit_id = "${c.commit.raw_id}"; + var f_path = "${c.f_path}"; + + checkFileHead($('#eform'), commit_id, f_path, 'edit') + }); diff --git a/rhodecode/templates/files/files_source.mako b/rhodecode/templates/files/files_source.mako --- a/rhodecode/templates/files/files_source.mako +++ b/rhodecode/templates/files/files_source.mako @@ -104,6 +104,7 @@ | ${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))} % if not c.file.is_binary: |${_('Copy content')} + |${_('Copy permalink')} % endif
    @@ -114,7 +115,9 @@ %if c.file.is_binary: <% rendered_binary = h.render_binary(c.repo_name, c.file)%> % if rendered_binary: +
    ${rendered_binary} +
    % else:
    ${_('Binary file ({})').format(c.file.mimetype)} diff --git a/rhodecode/templates/files/files_source_header.mako b/rhodecode/templates/files/files_source_header.mako --- a/rhodecode/templates/files/files_source_header.mako +++ b/rhodecode/templates/files/files_source_header.mako @@ -33,7 +33,7 @@

    ${_('File last commit')}:

    ${h.show_id(c.file_last_commit)} - ${file_base.refs(c.file_last_commit)} + ${file_base.refs(c.file_last_commit)}
    @@ -46,7 +46,7 @@ ${h.show_id(c.commit)} - ${file_base.refs(c.commit)} + ${file_base.refs(c.commit, request.GET.get('at'))} diff --git a/rhodecode/templates/files/files_tree_header.mako b/rhodecode/templates/files/files_tree_header.mako --- a/rhodecode/templates/files/files_tree_header.mako +++ b/rhodecode/templates/files/files_tree_header.mako @@ -33,7 +33,8 @@ - ${file_base.refs(c.commit)} + ${file_base.refs(c.commit, request.GET.get('at'))} + diff --git a/rhodecode/templates/files/files_upload.mako b/rhodecode/templates/files/files_upload.mako --- a/rhodecode/templates/files/files_upload.mako +++ b/rhodecode/templates/files/files_upload.mako @@ -106,7 +106,7 @@ ${field.end_sequence()} - \ No newline at end of file + diff --git a/rhodecode/templates/forms/checkbox_choice_desc.pt b/rhodecode/templates/forms/checkbox_choice_desc.pt new file mode 100644 --- /dev/null +++ b/rhodecode/templates/forms/checkbox_choice_desc.pt @@ -0,0 +1,26 @@ +
    + ${field.start_sequence()} +
    +
    + + +

    ${help_block}

    +
    +
    + ${field.end_sequence()} +
    diff --git a/rhodecode/templates/pullrequests/pullrequest.mako b/rhodecode/templates/pullrequests/pullrequest.mako --- a/rhodecode/templates/pullrequests/pullrequest.mako +++ b/rhodecode/templates/pullrequests/pullrequest.mako @@ -241,7 +241,93 @@ // custom code mirror var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm; + var diffDataHandler = function(data) { + + $('#pull_request_overview').html(data); + + var commitElements = data['commits']; + var files = data['files']; + var added = data['stats'][0] + var deleted = data['stats'][1] + var commonAncestorId = data['ancestor']; + + var prTitleAndDesc = getTitleAndDescription( + sourceRef()[1], commitElements, 5); + + var title = prTitleAndDesc[0]; + var proposedDescription = prTitleAndDesc[1]; + + var useGeneratedTitle = ( + $('#pullrequest_title').hasClass('autogenerated-title') || + $('#pullrequest_title').val() === ""); + + if (title && useGeneratedTitle) { + // use generated title if we haven't specified our own + $('#pullrequest_title').val(title); + $('#pullrequest_title').addClass('autogenerated-title'); + + } + + var useGeneratedDescription = ( + !codeMirrorInstance._userDefinedValue || + codeMirrorInstance.getValue() === ""); + + if (proposedDescription && useGeneratedDescription) { + // set proposed content, if we haven't defined our own, + // or we don't have description written + codeMirrorInstance._userDefinedValue = false; // reset state + codeMirrorInstance.setValue(proposedDescription); + } + + // refresh our codeMirror so events kicks in and it's change aware + codeMirrorInstance.refresh(); + + var url_data = { + 'repo_name': targetRepo(), + 'target_repo': sourceRepo(), + 'source_ref': targetRef()[2], + 'source_ref_type': 'rev', + 'target_ref': sourceRef()[2], + 'target_ref_type': 'rev', + 'merge': true, + '_': Date.now() // bypass browser caching + }; // gather the source/target ref and repo here + var url = pyroutes.url('repo_compare', url_data); + + var msg = ''.format(commonAncestorId); + msg += '' + + $.each(commitElements, function(idx, value) { + msg += ''.format(value["raw_id"]); + }); + + msg += '' + msg += _ngettext( + 'This pull requests will consist of {0} commit.', + 'This pull requests will consist of {0} commits.', + commitElements.length).format(commitElements.length) + + msg += '\n'; + msg += _ngettext( + '{0} file changed, ', + '{0} files changed, ', + files.length).format(files.length) + msg += '{0} lines inserted, {1} lines deleted.'.format(added, deleted) + + msg += '\n\n ${_('Show detailed compare.')}'.format(url); + + if (commitElements.length) { + var commitsLink = '{0}'.format(commitElements.length); + prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare'); + } + else { + prButtonLock(true, "${_('There are no commits to merge.')}", 'compare'); + } + + }; + reviewersController = new ReviewersController(); + reviewersController.diffDataHandler = diffDataHandler; var queryTargetRepo = function(self, query) { // cache ALL results if query is empty @@ -260,9 +346,10 @@ self.cachedDataSource[cacheKey] = data; query.callback({results: data.results}); }, - error: function(data, textStatus, errorThrown) { - alert( - "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); + error: function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while fetching entries.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); } }); } @@ -285,99 +372,6 @@ query.callback({results: data.results}); }; - var loadRepoRefDiffPreview = function() { - - var url_data = { - 'repo_name': targetRepo(), - 'target_repo': sourceRepo(), - 'source_ref': targetRef()[2], - 'source_ref_type': 'rev', - 'target_ref': sourceRef()[2], - 'target_ref_type': 'rev', - 'merge': true, - '_': Date.now() // bypass browser caching - }; // gather the source/target ref and repo here - - if (sourceRef().length !== 3 || targetRef().length !== 3) { - prButtonLock(true, "${_('Please select source and target')}"); - return; - } - var url = pyroutes.url('repo_compare', url_data); - - // lock PR button, so we cannot send PR before it's calculated - prButtonLock(true, "${_('Loading compare ...')}", 'compare'); - - if (loadRepoRefDiffPreview._currentRequest) { - loadRepoRefDiffPreview._currentRequest.abort(); - } - - loadRepoRefDiffPreview._currentRequest = $.get(url) - .error(function(data, textStatus, errorThrown) { - if (textStatus !== 'abort') { - alert( - "Error while processing request.\nError code {0} ({1}).".format( - data.status, data.statusText)); - } - - }) - .done(function(data) { - loadRepoRefDiffPreview._currentRequest = null; - $('#pull_request_overview').html(data); - - var commitElements = $(data).find('tr[commit_id]'); - - var prTitleAndDesc = getTitleAndDescription( - sourceRef()[1], commitElements, 5); - - var title = prTitleAndDesc[0]; - var proposedDescription = prTitleAndDesc[1]; - - var useGeneratedTitle = ( - $('#pullrequest_title').hasClass('autogenerated-title') || - $('#pullrequest_title').val() === ""); - - if (title && useGeneratedTitle) { - // use generated title if we haven't specified our own - $('#pullrequest_title').val(title); - $('#pullrequest_title').addClass('autogenerated-title'); - - } - - var useGeneratedDescription = ( - !codeMirrorInstance._userDefinedValue || - codeMirrorInstance.getValue() === ""); - - if (proposedDescription && useGeneratedDescription) { - // set proposed content, if we haven't defined our own, - // or we don't have description written - codeMirrorInstance._userDefinedValue = false; // reset state - codeMirrorInstance.setValue(proposedDescription); - } - - // refresh our codeMirror so events kicks in and it's change aware - codeMirrorInstance.refresh(); - - var msg = ''; - if (commitElements.length === 1) { - msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}"; - } else { - msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}"; - } - - msg += ' ${_('Show detailed compare.')}'.format(url); - - if (commitElements.length) { - var commitsLink = '{0}'.format(commitElements.length); - prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare'); - } - else { - prButtonLock(true, "${_('There are no commits to merge.')}", 'compare'); - } - - - }); - }; - var Select2Box = function(element, overrides) { var globalDefaults = { dropdownAutoWidth: true, @@ -475,13 +469,11 @@ targetRepoSelect2.initRepo(defaultTargetRepo, false); $sourceRef.on('change', function(e){ - loadRepoRefDiffPreview(); reviewersController.loadDefaultReviewers( sourceRepo(), sourceRef(), targetRepo(), targetRef()); }); $targetRef.on('change', function(e){ - loadRepoRefDiffPreview(); reviewersController.loadDefaultReviewers( sourceRepo(), sourceRef(), targetRepo(), targetRef()); }); @@ -501,10 +493,11 @@ success: function(data) { $('#target_ref_loading').hide(); targetRepoChanged(data); - loadRepoRefDiffPreview(); }, - error: function(data, textStatus, errorThrown) { - alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); + error: function(jqXHR, textStatus, errorThrown) { + var prefix = "Error while fetching entries.\n" + var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); + ajaxErrorSwal(message); } }) @@ -530,8 +523,8 @@ % if c.default_source_ref: // in case we have a pre-selected value, use it now $sourceRef.select2('val', '${c.default_source_ref}'); - // diff preview load - loadRepoRefDiffPreview(); + + // default reviewers reviewersController.loadDefaultReviewers( sourceRepo(), sourceRef(), targetRepo(), targetRef()); diff --git a/rhodecode/templates/pullrequests/pullrequest_show.mako b/rhodecode/templates/pullrequests/pullrequest_show.mako --- a/rhodecode/templates/pullrequests/pullrequest_show.mako +++ b/rhodecode/templates/pullrequests/pullrequest_show.mako @@ -11,15 +11,6 @@ <%def name="breadcrumbs_links()"> -
    - % if c.pull_request.is_closed(): - ${_('Closed')} - % endif - -
    - <%def name="menu_bar_nav()"> @@ -40,11 +31,19 @@
    - ${self.breadcrumbs()} -
    +
    + % if c.pull_request.is_closed(): + ${_('Closed')} + % endif + +
    + + <% summary = lambda n:{False:'summary-short'}.get(n) %>
    @@ -64,8 +63,9 @@ ## REVIEWERS -
    +
    ${_('Pull request reviewers')} %if c.allowed_to_update: @@ -471,6 +471,16 @@
    + % elif c.pr_merge_source_commit.changed: +
    +
    +
    + % if c.pr_merge_source_commit.changed: + ${_('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)} + % endif +
    +
    +
    % endif
    diff --git a/rhodecode/tests/__init__.py b/rhodecode/tests/__init__.py --- a/rhodecode/tests/__init__.py +++ b/rhodecode/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/auth_external_test.py b/rhodecode/tests/auth_external_test.py --- a/rhodecode/tests/auth_external_test.py +++ b/rhodecode/tests/auth_external_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/__init__.py b/rhodecode/tests/config/__init__.py --- a/rhodecode/tests/config/__init__.py +++ b/rhodecode/tests/config/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_environment.py b/rhodecode/tests/config/test_environment.py --- a/rhodecode/tests/config/test_environment.py +++ b/rhodecode/tests/config/test_environment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_routing_links.py b/rhodecode/tests/config/test_routing_links.py --- a/rhodecode/tests/config/test_routing_links.py +++ b/rhodecode/tests/config/test_routing_links.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_sanitize_settings.py b/rhodecode/tests/config/test_sanitize_settings.py --- a/rhodecode/tests/config/test_sanitize_settings.py +++ b/rhodecode/tests/config/test_sanitize_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_utils.py b/rhodecode/tests/config/test_utils.py --- a/rhodecode/tests/config/test_utils.py +++ b/rhodecode/tests/config/test_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012-2019 RhodeCode GmbH +# Copyright (C) 2012-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/conftest.py b/rhodecode/tests/database/conftest.py --- a/rhodecode/tests/database/conftest.py +++ b/rhodecode/tests/database/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/test_creation.py b/rhodecode/tests/database/test_creation.py --- a/rhodecode/tests/database/test_creation.py +++ b/rhodecode/tests/database/test_creation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/test_migration.py b/rhodecode/tests/database/test_migration.py --- a/rhodecode/tests/database/test_migration.py +++ b/rhodecode/tests/database/test_migration.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/__init__.py b/rhodecode/tests/events/__init__.py --- a/rhodecode/tests/events/__init__.py +++ b/rhodecode/tests/events/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/conftest.py b/rhodecode/tests/events/conftest.py --- a/rhodecode/tests/events/conftest.py +++ b/rhodecode/tests/events/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/test_pullrequest.py b/rhodecode/tests/events/test_pullrequest.py --- a/rhodecode/tests/events/test_pullrequest.py +++ b/rhodecode/tests/events/test_pullrequest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -40,7 +40,7 @@ from rhodecode.events import ( PullRequestUpdateEvent, PullRequestReviewEvent, PullRequestMergeEvent, - PullRequestCloseEvent, + PullRequestCloseEvent ]) def test_pullrequest_events_serialized(EventClass, pr_util, config_stub): pr = pr_util.create_pull_request() diff --git a/rhodecode/tests/events/test_repo.py b/rhodecode/tests/events/test_repo.py --- a/rhodecode/tests/events/test_repo.py +++ b/rhodecode/tests/events/test_repo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -20,6 +20,7 @@ import pytest +from rhodecode.lib.utils2 import StrictAttributeDict from rhodecode.tests.events.conftest import EventCatcher from rhodecode.lib import hooks_base, utils2 @@ -28,7 +29,7 @@ from rhodecode.events.repo import ( RepoPrePullEvent, RepoPullEvent, RepoPrePushEvent, RepoPushEvent, RepoPreCreateEvent, RepoCreateEvent, - RepoPreDeleteEvent, RepoDeleteEvent, + RepoPreDeleteEvent, RepoDeleteEvent, RepoCommitCommentEvent, ) @@ -121,3 +122,24 @@ def test_push_fires_events(scm_extras): hooks_base.post_pull(scm_extras) assert event_catcher.events_types == [RepoPullEvent] + +@pytest.mark.parametrize('EventClass', [RepoCommitCommentEvent]) +def test_repo_commit_event(config_stub, repo_stub, EventClass): + + commit = StrictAttributeDict({ + 'raw_id': 'raw_id', + 'message': 'message', + 'branch': 'branch', + }) + + comment = StrictAttributeDict({ + 'comment_id': 'comment_id', + 'text': 'text', + 'comment_type': 'comment_type', + 'f_path': 'f_path', + 'line_no': 'line_no', + }) + event = EventClass(repo=repo_stub, commit=commit, comment=comment) + data = event.as_dict() + assert data['commit']['commit_id'] + assert data['comment']['comment_id'] diff --git a/rhodecode/tests/fixture.py b/rhodecode/tests/fixture.py --- a/rhodecode/tests/fixture.py +++ b/rhodecode/tests/fixture.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/fixtures/git_node_history_response.json b/rhodecode/tests/fixtures/git_node_history_response.json --- a/rhodecode/tests/fixtures/git_node_history_response.json +++ b/rhodecode/tests/fixtures/git_node_history_response.json @@ -1,1 +1,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} +{"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} diff --git a/rhodecode/tests/fixtures/hg_node_history_response.json b/rhodecode/tests/fixtures/hg_node_history_response.json --- a/rhodecode/tests/fixtures/hg_node_history_response.json +++ b/rhodecode/tests/fixtures/hg_node_history_response.json @@ -1,1 +1,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 +{"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 diff --git a/rhodecode/tests/fixtures/svn_node_history_branches.json b/rhodecode/tests/fixtures/svn_node_history_branches.json --- a/rhodecode/tests/fixtures/svn_node_history_branches.json +++ b/rhodecode/tests/fixtures/svn_node_history_branches.json @@ -1,1 +1,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 +{"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 diff --git a/rhodecode/tests/fixtures/svn_node_history_response.json b/rhodecode/tests/fixtures/svn_node_history_response.json --- a/rhodecode/tests/fixtures/svn_node_history_response.json +++ b/rhodecode/tests/fixtures/svn_node_history_response.json @@ -1,1 +1,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 +{"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 diff --git a/rhodecode/tests/functional/__init__.py b/rhodecode/tests/functional/__init__.py --- a/rhodecode/tests/functional/__init__.py +++ b/rhodecode/tests/functional/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/test_bad_request_data.py b/rhodecode/tests/functional/test_bad_request_data.py --- a/rhodecode/tests/functional/test_bad_request_data.py +++ b/rhodecode/tests/functional/test_bad_request_data.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/test_delegated_admin.py b/rhodecode/tests/functional/test_delegated_admin.py --- a/rhodecode/tests/functional/test_delegated_admin.py +++ b/rhodecode/tests/functional/test_delegated_admin.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/test_sessions.py b/rhodecode/tests/functional/test_sessions.py --- a/rhodecode/tests/functional/test_sessions.py +++ b/rhodecode/tests/functional/test_sessions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/conftest.py b/rhodecode/tests/integrations/conftest.py --- a/rhodecode/tests/integrations/conftest.py +++ b/rhodecode/tests/integrations/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/test_integration.py b/rhodecode/tests/integrations/test_integration.py --- a/rhodecode/tests/integrations/test_integration.py +++ b/rhodecode/tests/integrations/test_integration.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -49,6 +49,7 @@ class TestDeleteScopesDeletesIntegration count = 1 + def counter(): global count val = count diff --git a/rhodecode/tests/integrations/test_slack.py b/rhodecode/tests/integrations/test_slack.py --- a/rhodecode/tests/integrations/test_slack.py +++ b/rhodecode/tests/integrations/test_slack.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/test_webhook.py b/rhodecode/tests/integrations/test_webhook.py --- a/rhodecode/tests/integrations/test_webhook.py +++ b/rhodecode/tests/integrations/test_webhook.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -52,8 +52,7 @@ def test_webhook_parse_url_invalid_event handler(event, {}) err = str(err.value) - assert err.startswith( - 'event type `%s` not in supported list' % event.__class__) + assert err == "event type `` has no handler defined" @pytest.mark.parametrize('template,expected_urls', [ diff --git a/rhodecode/tests/lib/__init__.py b/rhodecode/tests/lib/__init__.py --- a/rhodecode/tests/lib/__init__.py +++ b/rhodecode/tests/lib/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/auth_modules/test_auth_modules.py b/rhodecode/tests/lib/auth_modules/test_auth_modules.py --- a/rhodecode/tests/lib/auth_modules/test_auth_modules.py +++ b/rhodecode/tests/lib/auth_modules/test_auth_modules.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/__init__.py b/rhodecode/tests/lib/middleware/__init__.py --- a/rhodecode/tests/lib/middleware/__init__.py +++ b/rhodecode/tests/lib/middleware/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/mock_scm_app.py b/rhodecode/tests/lib/middleware/mock_scm_app.py --- a/rhodecode/tests/lib/middleware/mock_scm_app.py +++ b/rhodecode/tests/lib/middleware/mock_scm_app.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_appenlight.py b/rhodecode/tests/lib/middleware/test_appenlight.py --- a/rhodecode/tests/lib/middleware/test_appenlight.py +++ b/rhodecode/tests/lib/middleware/test_appenlight.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplegit.py b/rhodecode/tests/lib/middleware/test_simplegit.py --- a/rhodecode/tests/lib/middleware/test_simplegit.py +++ b/rhodecode/tests/lib/middleware/test_simplegit.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplehg.py b/rhodecode/tests/lib/middleware/test_simplehg.py --- a/rhodecode/tests/lib/middleware/test_simplehg.py +++ b/rhodecode/tests/lib/middleware/test_simplehg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplesvn.py b/rhodecode/tests/lib/middleware/test_simplesvn.py --- a/rhodecode/tests/lib/middleware/test_simplesvn.py +++ b/rhodecode/tests/lib/middleware/test_simplesvn.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplevcs.py b/rhodecode/tests/lib/middleware/test_simplevcs.py --- a/rhodecode/tests/lib/middleware/test_simplevcs.py +++ b/rhodecode/tests/lib/middleware/test_simplevcs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_vcs.py b/rhodecode/tests/lib/middleware/test_vcs.py --- a/rhodecode/tests/lib/middleware/test_vcs.py +++ b/rhodecode/tests/lib/middleware/test_vcs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_vcs_unavailable.py b/rhodecode/tests/lib/middleware/test_vcs_unavailable.py --- a/rhodecode/tests/lib/middleware/test_vcs_unavailable.py +++ b/rhodecode/tests/lib/middleware/test_vcs_unavailable.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/__init__.py b/rhodecode/tests/lib/middleware/utils/__init__.py --- a/rhodecode/tests/lib/middleware/utils/__init__.py +++ b/rhodecode/tests/lib/middleware/utils/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py b/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py --- a/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py +++ b/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py b/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py --- a/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py +++ b/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py b/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py --- a/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py +++ b/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_action_parser.py b/rhodecode/tests/lib/test_action_parser.py --- a/rhodecode/tests/lib/test_action_parser.py +++ b/rhodecode/tests/lib/test_action_parser.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_audit_logger.py b/rhodecode/tests/lib/test_audit_logger.py --- a/rhodecode/tests/lib/test_audit_logger.py +++ b/rhodecode/tests/lib/test_audit_logger.py @@ -1,7 +1,7 @@ import collections # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_auth.py b/rhodecode/tests/lib/test_auth.py --- a/rhodecode/tests/lib/test_auth.py +++ b/rhodecode/tests/lib/test_auth.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -147,7 +147,7 @@ def test_cached_perms_data_repository_pe assert permissions['repositories'][repo.repo_name] == 'repository.admin' # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack - repo.user_id = User.get_default_user().user_id + repo.user_id = User.get_default_user_id() def test_cached_perms_data_repository_permissions_not_inheriting_defaults( diff --git a/rhodecode/tests/lib/test_auth_crypto_backend.py b/rhodecode/tests/lib/test_auth_crypto_backend.py --- a/rhodecode/tests/lib/test_auth_crypto_backend.py +++ b/rhodecode/tests/lib/test_auth_crypto_backend.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_base.py b/rhodecode/tests/lib/test_base.py --- a/rhodecode/tests/lib/test_base.py +++ b/rhodecode/tests/lib/test_base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_caches.py b/rhodecode/tests/lib/test_caches.py --- a/rhodecode/tests/lib/test_caches.py +++ b/rhodecode/tests/lib/test_caches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_codeblocks.py b/rhodecode/tests/lib/test_codeblocks.py --- a/rhodecode/tests/lib/test_codeblocks.py +++ b/rhodecode/tests/lib/test_codeblocks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_colored_formatter.py b/rhodecode/tests/lib/test_colored_formatter.py --- a/rhodecode/tests/lib/test_colored_formatter.py +++ b/rhodecode/tests/lib/test_colored_formatter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_datelib.py b/rhodecode/tests/lib/test_datelib.py --- a/rhodecode/tests/lib/test_datelib.py +++ b/rhodecode/tests/lib/test_datelib.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_db_manage.py b/rhodecode/tests/lib/test_db_manage.py --- a/rhodecode/tests/lib/test_db_manage.py +++ b/rhodecode/tests/lib/test_db_manage.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_dbmigrate.py b/rhodecode/tests/lib/test_dbmigrate.py --- a/rhodecode/tests/lib/test_dbmigrate.py +++ b/rhodecode/tests/lib/test_dbmigrate.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_diffs.py b/rhodecode/tests/lib/test_diffs.py --- a/rhodecode/tests/lib/test_diffs.py +++ b/rhodecode/tests/lib/test_diffs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_diffs_context.py b/rhodecode/tests/lib/test_diffs_context.py --- a/rhodecode/tests/lib/test_diffs_context.py +++ b/rhodecode/tests/lib/test_diffs_context.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_encrypt.py b/rhodecode/tests/lib/test_encrypt.py --- a/rhodecode/tests/lib/test_encrypt.py +++ b/rhodecode/tests/lib/test_encrypt.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_ext_json.py b/rhodecode/tests/lib/test_ext_json.py --- a/rhodecode/tests/lib/test_ext_json.py +++ b/rhodecode/tests/lib/test_ext_json.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_graphmod.py b/rhodecode/tests/lib/test_graphmod.py --- a/rhodecode/tests/lib/test_graphmod.py +++ b/rhodecode/tests/lib/test_graphmod.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_helpers.py b/rhodecode/tests/lib/test_helpers.py --- a/rhodecode/tests/lib/test_helpers.py +++ b/rhodecode/tests/lib/test_helpers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_hooks_base.py b/rhodecode/tests/lib/test_hooks_base.py --- a/rhodecode/tests/lib/test_hooks_base.py +++ b/rhodecode/tests/lib/test_hooks_base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_hooks_daemon.py b/rhodecode/tests/lib/test_hooks_daemon.py --- a/rhodecode/tests/lib/test_hooks_daemon.py +++ b/rhodecode/tests/lib/test_hooks_daemon.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_jsonalchemy.py b/rhodecode/tests/lib/test_jsonalchemy.py --- a/rhodecode/tests/lib/test_jsonalchemy.py +++ b/rhodecode/tests/lib/test_jsonalchemy.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_libs.py b/rhodecode/tests/lib/test_libs.py --- a/rhodecode/tests/lib/test_libs.py +++ b/rhodecode/tests/lib/test_libs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_mako_emails.py b/rhodecode/tests/lib/test_mako_emails.py --- a/rhodecode/tests/lib/test_mako_emails.py +++ b/rhodecode/tests/lib/test_mako_emails.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_markup_renderer.py b/rhodecode/tests/lib/test_markup_renderer.py --- a/rhodecode/tests/lib/test_markup_renderer.py +++ b/rhodecode/tests/lib/test_markup_renderer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_search.py b/rhodecode/tests/lib/test_search.py --- a/rhodecode/tests/lib/test_search.py +++ b/rhodecode/tests/lib/test_search.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_search_utils.py b/rhodecode/tests/lib/test_search_utils.py --- a/rhodecode/tests/lib/test_search_utils.py +++ b/rhodecode/tests/lib/test_search_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_utils.py b/rhodecode/tests/lib/test_utils.py --- a/rhodecode/tests/lib/test_utils.py +++ b/rhodecode/tests/lib/test_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/api.py b/rhodecode/tests/load/api.py --- a/rhodecode/tests/load/api.py +++ b/rhodecode/tests/load/api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/http_performance.py b/rhodecode/tests/load/http_performance.py --- a/rhodecode/tests/load/http_performance.py +++ b/rhodecode/tests/load/http_performance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/profile-mem.py b/rhodecode/tests/load/profile-mem.py --- a/rhodecode/tests/load/profile-mem.py +++ b/rhodecode/tests/load/profile-mem.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/profile.py b/rhodecode/tests/load/profile.py --- a/rhodecode/tests/load/profile.py +++ b/rhodecode/tests/load/profile.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/time_urls.py b/rhodecode/tests/load/time_urls.py --- a/rhodecode/tests/load/time_urls.py +++ b/rhodecode/tests/load/time_urls.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/vcs_performance.py b/rhodecode/tests/load/vcs_performance.py --- a/rhodecode/tests/load/vcs_performance.py +++ b/rhodecode/tests/load/vcs_performance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/__init__.py b/rhodecode/tests/models/__init__.py --- a/rhodecode/tests/models/__init__.py +++ b/rhodecode/tests/models/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/common.py b/rhodecode/tests/models/common.py --- a/rhodecode/tests/models/common.py +++ b/rhodecode/tests/models/common.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_gist_schema.py b/rhodecode/tests/models/schemas/test_gist_schema.py --- a/rhodecode/tests/models/schemas/test_gist_schema.py +++ b/rhodecode/tests/models/schemas/test_gist_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_integration_schema.py b/rhodecode/tests/models/schemas/test_integration_schema.py --- a/rhodecode/tests/models/schemas/test_integration_schema.py +++ b/rhodecode/tests/models/schemas/test_integration_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_repo_group_schema.py b/rhodecode/tests/models/schemas/test_repo_group_schema.py --- a/rhodecode/tests/models/schemas/test_repo_group_schema.py +++ b/rhodecode/tests/models/schemas/test_repo_group_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_repo_schema.py b/rhodecode/tests/models/schemas/test_repo_schema.py --- a/rhodecode/tests/models/schemas/test_repo_schema.py +++ b/rhodecode/tests/models/schemas/test_repo_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_schema_types.py b/rhodecode/tests/models/schemas/test_schema_types.py --- a/rhodecode/tests/models/schemas/test_schema_types.py +++ b/rhodecode/tests/models/schemas/test_schema_types.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_user_schema.py b/rhodecode/tests/models/schemas/test_user_schema.py --- a/rhodecode/tests/models/schemas/test_user_schema.py +++ b/rhodecode/tests/models/schemas/test_user_schema.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_user_usergroup_types.py b/rhodecode/tests/models/schemas/test_user_usergroup_types.py --- a/rhodecode/tests/models/schemas/test_user_usergroup_types.py +++ b/rhodecode/tests/models/schemas/test_user_usergroup_types.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/__init__.py b/rhodecode/tests/models/settings/__init__.py --- a/rhodecode/tests/models/settings/__init__.py +++ b/rhodecode/tests/models/settings/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/test_issue_tracker.py b/rhodecode/tests/models/settings/test_issue_tracker.py --- a/rhodecode/tests/models/settings/test_issue_tracker.py +++ b/rhodecode/tests/models/settings/test_issue_tracker.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/test_settings.py b/rhodecode/tests/models/settings/test_settings.py --- a/rhodecode/tests/models/settings/test_settings.py +++ b/rhodecode/tests/models/settings/test_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/test_vcs_settings.py b/rhodecode/tests/models/settings/test_vcs_settings.py --- a/rhodecode/tests/models/settings/test_vcs_settings.py +++ b/rhodecode/tests/models/settings/test_vcs_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_changeset_status.py b/rhodecode/tests/models/test_changeset_status.py --- a/rhodecode/tests/models/test_changeset_status.py +++ b/rhodecode/tests/models/test_changeset_status.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_changeset_status_group_voting.py b/rhodecode/tests/models/test_changeset_status_group_voting.py --- a/rhodecode/tests/models/test_changeset_status_group_voting.py +++ b/rhodecode/tests/models/test_changeset_status_group_voting.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_comment.py b/rhodecode/tests/models/test_comment.py --- a/rhodecode/tests/models/test_comment.py +++ b/rhodecode/tests/models/test_comment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_db.py b/rhodecode/tests/models/test_db.py --- a/rhodecode/tests/models/test_db.py +++ b/rhodecode/tests/models/test_db.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_gist.py b/rhodecode/tests/models/test_gist.py --- a/rhodecode/tests/models/test_gist.py +++ b/rhodecode/tests/models/test_gist.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_notifications.py b/rhodecode/tests/models/test_notifications.py --- a/rhodecode/tests/models/test_notifications.py +++ b/rhodecode/tests/models/test_notifications.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_permissions.py b/rhodecode/tests/models/test_permissions.py --- a/rhodecode/tests/models/test_permissions.py +++ b/rhodecode/tests/models/test_permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_pullrequest.py b/rhodecode/tests/models/test_pullrequest.py --- a/rhodecode/tests/models/test_pullrequest.py +++ b/rhodecode/tests/models/test_pullrequest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -169,7 +169,7 @@ class TestPullRequestModel(object): assert pull_request._last_merge_target_rev is None assert pull_request.last_merge_status is None - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is True assert msg == 'This pull request can be automatically merged.' self.merge_mock.assert_called_with( @@ -184,7 +184,7 @@ class TestPullRequestModel(object): assert pull_request.last_merge_status is MergeFailureReason.NONE self.merge_mock.reset_mock() - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is True assert msg == 'This pull request can be automatically merged.' assert self.merge_mock.called is False @@ -198,7 +198,7 @@ class TestPullRequestModel(object): assert pull_request._last_merge_target_rev is None assert pull_request.last_merge_status is None - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False assert msg == 'This pull request cannot be merged because of merge conflicts. file1' self.merge_mock.assert_called_with( @@ -213,9 +213,9 @@ class TestPullRequestModel(object): assert pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED self.merge_mock.reset_mock() - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == 'This pull request cannot be merged because of merge conflicts. ' + assert msg == 'This pull request cannot be merged because of merge conflicts. file1' assert self.merge_mock.called is False def test_merge_status_unknown_failure(self, pull_request): @@ -227,7 +227,7 @@ class TestPullRequestModel(object): assert pull_request._last_merge_target_rev is None assert pull_request.last_merge_status is None - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False assert msg == ( 'This pull request cannot be merged because of an unhandled exception. ' @@ -244,7 +244,7 @@ class TestPullRequestModel(object): assert pull_request.last_merge_status is None self.merge_mock.reset_mock() - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False assert msg == ( 'This pull request cannot be merged because of an unhandled exception. ' @@ -253,7 +253,7 @@ class TestPullRequestModel(object): def test_merge_status_when_target_is_locked(self, pull_request): pull_request.target_repo.locked = [1, u'12345.50', 'lock_web'] - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False assert msg == ( 'This pull request cannot be merged because the target repository ' @@ -266,7 +266,7 @@ class TestPullRequestModel(object): patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles) with patcher: - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False assert msg == 'Target repository large files support is disabled.' @@ -278,7 +278,7 @@ class TestPullRequestModel(object): patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles) with patcher: - status, msg = PullRequestModel().merge_status(pull_request) + merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False assert msg == 'Source repository large files support is disabled.' diff --git a/rhodecode/tests/models/test_pullrequest_git.py b/rhodecode/tests/models/test_pullrequest_git.py --- a/rhodecode/tests/models/test_pullrequest_git.py +++ b/rhodecode/tests/models/test_pullrequest_git.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_repo_groups.py b/rhodecode/tests/models/test_repo_groups.py --- a/rhodecode/tests/models/test_repo_groups.py +++ b/rhodecode/tests/models/test_repo_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_repo_readme.py b/rhodecode/tests/models/test_repo_readme.py --- a/rhodecode/tests/models/test_repo_readme.py +++ b/rhodecode/tests/models/test_repo_readme.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_repos.py b/rhodecode/tests/models/test_repos.py --- a/rhodecode/tests/models/test_repos.py +++ b/rhodecode/tests/models/test_repos.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_scm.py b/rhodecode/tests/models/test_scm.py --- a/rhodecode/tests/models/test_scm.py +++ b/rhodecode/tests/models/test_scm.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py b/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py --- a/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py +++ b/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_user_groups.py b/rhodecode/tests/models/test_user_groups.py --- a/rhodecode/tests/models/test_user_groups.py +++ b/rhodecode/tests/models/test_user_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_user_permissions_on_repo_groups.py b/rhodecode/tests/models/test_user_permissions_on_repo_groups.py --- a/rhodecode/tests/models/test_user_permissions_on_repo_groups.py +++ b/rhodecode/tests/models/test_user_permissions_on_repo_groups.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -49,7 +49,7 @@ def permissions_setup_func(group_name='g user_id = test_u1_id # called by the @with_setup decorator also reset the default user stuff permissions_setup_func(group_name, perm, recursive, - user_id=User.get_default_user().user_id) + user_id=User.get_default_user_id()) # TODO: DRY, compare test_user_group:permissions_setup_func repo_group = RepoGroup.get_by_group_name(group_name=group_name) @@ -142,7 +142,7 @@ def test_user_permissions_on_group_with_ # other repos and groups should have this permission now set ! recursive = 'all' group = 'g0' - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() permissions_setup_func(group, 'group.write', recursive=recursive, user_id=default_user_id) @@ -221,7 +221,7 @@ def test_user_permissions_on_group_with_ recursive = 'repos' group = 'g0/g0_1' perm = 'group.none' - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() # TODO: workaround due to different setup calls, adept to py.test style permissions_setup_func() @@ -281,7 +281,7 @@ def test_user_permissions_on_group_with_ # should remain intact as we use groups only mode ! recursive = 'groups' group = 'g0/g0_1' - default_user_id = User.get_default_user().user_id + default_user_id = User.get_default_user_id() # TODO: workaround due to different setup calls, adept to py.test style permissions_setup_func() diff --git a/rhodecode/tests/models/test_users.py b/rhodecode/tests/models/test_users.py --- a/rhodecode/tests/models/test_users.py +++ b/rhodecode/tests/models/test_users.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/__init__.py b/rhodecode/tests/other/__init__.py --- a/rhodecode/tests/other/__init__.py +++ b/rhodecode/tests/other/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/test_base_controller.py b/rhodecode/tests/other/test_base_controller.py --- a/rhodecode/tests/other/test_base_controller.py +++ b/rhodecode/tests/other/test_base_controller.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/test_validators.py b/rhodecode/tests/other/test_validators.py --- a/rhodecode/tests/other/test_validators.py +++ b/rhodecode/tests/other/test_validators.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/test_views_utils.py b/rhodecode/tests/other/test_views_utils.py --- a/rhodecode/tests/other/test_views_utils.py +++ b/rhodecode/tests/other/test_views_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/plugin.py b/rhodecode/tests/plugin.py --- a/rhodecode/tests/plugin.py +++ b/rhodecode/tests/plugin.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/pylons_plugin.py b/rhodecode/tests/pylons_plugin.py --- a/rhodecode/tests/pylons_plugin.py +++ b/rhodecode/tests/pylons_plugin.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/scripts/test_concurency.py b/rhodecode/tests/scripts/test_concurency.py --- a/rhodecode/tests/scripts/test_concurency.py +++ b/rhodecode/tests/scripts/test_concurency.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/scripts/test_crawler.py b/rhodecode/tests/scripts/test_crawler.py --- a/rhodecode/tests/scripts/test_crawler.py +++ b/rhodecode/tests/scripts/test_crawler.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/server_utils.py b/rhodecode/tests/server_utils.py --- a/rhodecode/tests/server_utils.py +++ b/rhodecode/tests/server_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/utils.py b/rhodecode/tests/utils.py --- a/rhodecode/tests/utils.py +++ b/rhodecode/tests/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/__init__.py b/rhodecode/tests/vcs/__init__.py --- a/rhodecode/tests/vcs/__init__.py +++ b/rhodecode/tests/vcs/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/conftest.py b/rhodecode/tests/vcs/conftest.py --- a/rhodecode/tests/vcs/conftest.py +++ b/rhodecode/tests/vcs/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_archives.py b/rhodecode/tests/vcs/test_archives.py --- a/rhodecode/tests/vcs/test_archives.py +++ b/rhodecode/tests/vcs/test_archives.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_branches.py b/rhodecode/tests/vcs/test_branches.py --- a/rhodecode/tests/vcs/test_branches.py +++ b/rhodecode/tests/vcs/test_branches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_client_http.py b/rhodecode/tests/vcs/test_client_http.py --- a/rhodecode/tests/vcs/test_client_http.py +++ b/rhodecode/tests/vcs/test_client_http.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_commits.py b/rhodecode/tests/vcs/test_commits.py --- a/rhodecode/tests/vcs/test_commits.py +++ b/rhodecode/tests/vcs/test_commits.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_config.py b/rhodecode/tests/vcs/test_config.py --- a/rhodecode/tests/vcs/test_config.py +++ b/rhodecode/tests/vcs/test_config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_diff.py b/rhodecode/tests/vcs/test_diff.py --- a/rhodecode/tests/vcs/test_diff.py +++ b/rhodecode/tests/vcs/test_diff.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_exceptions.py b/rhodecode/tests/vcs/test_exceptions.py --- a/rhodecode/tests/vcs/test_exceptions.py +++ b/rhodecode/tests/vcs/test_exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_filenodes_unicode_path.py b/rhodecode/tests/vcs/test_filenodes_unicode_path.py --- a/rhodecode/tests/vcs/test_filenodes_unicode_path.py +++ b/rhodecode/tests/vcs/test_filenodes_unicode_path.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_getitem.py b/rhodecode/tests/vcs/test_getitem.py --- a/rhodecode/tests/vcs/test_getitem.py +++ b/rhodecode/tests/vcs/test_getitem.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_getslice.py b/rhodecode/tests/vcs/test_getslice.py --- a/rhodecode/tests/vcs/test_getslice.py +++ b/rhodecode/tests/vcs/test_getslice.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py --- a/rhodecode/tests/vcs/test_git.py +++ b/rhodecode/tests/vcs/test_git.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_hg.py b/rhodecode/tests/vcs/test_hg.py --- a/rhodecode/tests/vcs/test_hg.py +++ b/rhodecode/tests/vcs/test_hg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py b/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py --- a/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py +++ b/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_inmemory.py b/rhodecode/tests/vcs/test_inmemory.py --- a/rhodecode/tests/vcs/test_inmemory.py +++ b/rhodecode/tests/vcs/test_inmemory.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_nodes.py b/rhodecode/tests/vcs/test_nodes.py --- a/rhodecode/tests/vcs/test_nodes.py +++ b/rhodecode/tests/vcs/test_nodes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_repository.py b/rhodecode/tests/vcs/test_repository.py --- a/rhodecode/tests/vcs/test_repository.py +++ b/rhodecode/tests/vcs/test_repository.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_svn.py b/rhodecode/tests/vcs/test_svn.py --- a/rhodecode/tests/vcs/test_svn.py +++ b/rhodecode/tests/vcs/test_svn.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_tags.py b/rhodecode/tests/vcs/test_tags.py --- a/rhodecode/tests/vcs/test_tags.py +++ b/rhodecode/tests/vcs/test_tags.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_utils.py b/rhodecode/tests/vcs/test_utils.py --- a/rhodecode/tests/vcs/test_utils.py +++ b/rhodecode/tests/vcs/test_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_vcs.py b/rhodecode/tests/vcs/test_vcs.py --- a/rhodecode/tests/vcs/test_vcs.py +++ b/rhodecode/tests/vcs/test_vcs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/utils.py b/rhodecode/tests/vcs/utils.py --- a/rhodecode/tests/vcs/utils.py +++ b/rhodecode/tests/vcs/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/__init__.py b/rhodecode/tests/vcs_operations/__init__.py --- a/rhodecode/tests/vcs_operations/__init__.py +++ b/rhodecode/tests/vcs_operations/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/conftest.py b/rhodecode/tests/vcs_operations/conftest.py --- a/rhodecode/tests/vcs_operations/conftest.py +++ b/rhodecode/tests/vcs_operations/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py b/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py --- a/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations.py b/rhodecode/tests/vcs_operations/test_vcs_operations.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py b/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py b/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py b/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcsserver_http.ini b/rhodecode/tests/vcsserver_http.ini --- a/rhodecode/tests/vcsserver_http.ini +++ b/rhodecode/tests/vcsserver_http.ini @@ -1,18 +1,89 @@ -################################################################################ -# RhodeCode VCSServer with HTTP Backend - configuration # -# # -################################################################################ +## -*- coding: utf-8 -*- + +; ################################# +; RHODECODE VCSSERVER CONFIGURATION +; ################################# + +[server:main] +; COMMON HOST/IP CONFIG +host = 127.0.0.1 +port = 9900 + + +; ########################### +; GUNICORN APPLICATION SERVER +; ########################### + +; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini + +; Module to use, this setting shouldn't be changed +use = egg:gunicorn#main [app:main] +; The %(here)s variable will be replaced with the absolute path of parent directory +; of this file use = egg:rhodecode-vcsserver +; Pyramid default locales, we need this to be set pyramid.default_locale_name = en -pyramid.includes = -# default locale used by VCS systems +; default locale used by VCS systems locale = en_US.UTF-8 -# cache regions, please don't change +; path to binaries for vcsserver, it should be set by the installer +; at installation time, e.g /home/user/vcsserver-1/profile/bin +; it can also be a path to nix-build output in case of development +core.binary_dir = "" + +; Custom exception store path, defaults to TMPDIR +; This is used to store exception from RhodeCode in shared directory +#exception_tracker.store_path = + +; ############# +; DOGPILE CACHE +; ############# + +; Default cache dir for caches. Putting this into a ramdisk can boost performance. +; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space +cache_dir = %(here)s/data + +; *************************************** +; `repo_object` cache, default file based +; *************************************** + +; `repo_object` cache settings for vcs methods for repositories +rc_cache.repo_object.backend = dogpile.cache.rc.memory_lru + +; cache auto-expires after N seconds +; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days) +rc_cache.repo_object.expiration_time = 2592000 + +; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set +#rc_cache.repo_object.arguments.filename = /tmp/vcsserver_cache.db + +; *********************************************************** +; `repo_object` cache with redis backend +; recommended for larger instance, and for better performance +; *********************************************************** + +; `repo_object` cache settings for vcs methods for repositories +#rc_cache.repo_object.backend = dogpile.cache.rc.redis_msgpack + +; cache auto-expires after N seconds +; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days) +#rc_cache.repo_object.expiration_time = 2592000 + +; redis_expiration_time needs to be greater then expiration_time +#rc_cache.repo_object.arguments.redis_expiration_time = 3592000 + +#rc_cache.repo_object.arguments.host = localhost +#rc_cache.repo_object.arguments.port = 6379 +#rc_cache.repo_object.arguments.db = 5 +#rc_cache.repo_object.arguments.socket_timeout = 30 +; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends +#rc_cache.repo_object.arguments.distributed_lock = true + +# legacy cache regions, please don't change beaker.cache.regions = repo_object beaker.cache.repo_object.type = memorylru beaker.cache.repo_object.max_items = 100 @@ -20,17 +91,13 @@ beaker.cache.repo_object.max_items = 100 beaker.cache.repo_object.expire = 300 beaker.cache.repo_object.enabled = true -[server:main] -host = 127.0.0.1 -port = 9900 -use = egg:gunicorn#main -################################ -### LOGGING CONFIGURATION #### -################################ +; ##################### +; LOGGING CONFIGURATION +; ##################### [loggers] -keys = root, vcsserver, beaker +keys = root, vcsserver [handlers] keys = console @@ -38,9 +105,9 @@ keys = console [formatters] keys = generic -############# -## LOGGERS ## -############# +; ####### +; LOGGERS +; ####### [logger_root] level = NOTSET handlers = console @@ -51,27 +118,21 @@ handlers = qualname = vcsserver propagate = 1 -[logger_beaker] -level = DEBUG -handlers = -qualname = beaker -propagate = 1 - -############## -## HANDLERS ## -############## +; ######## +; HANDLERS +; ######## [handler_console] class = StreamHandler -args = (sys.stderr,) +args = (sys.stderr, ) level = DEBUG formatter = generic -################ -## FORMATTERS ## -################ +; ########## +; FORMATTERS +; ########## [formatter_generic] -format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s +format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s datefmt = %Y-%m-%d %H:%M:%S diff --git a/rhodecode/translation.py b/rhodecode/translation.py --- a/rhodecode/translation.py +++ b/rhodecode/translation.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 RhodeCode GmbH +# Copyright (C) 2016-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tweens.py b/rhodecode/tweens.py --- a/rhodecode/tweens.py +++ b/rhodecode/tweens.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2019 RhodeCode GmbH +# Copyright (C) 2010-2020 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3