##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r802:f6088673 merge stable
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,15 b''
1 {
2 "name": "rhodecode-elements",
3 "description": "User interface for elements for rhodecode",
4 "main": "index.html",
5 "dependencies": {
6 "webcomponentsjs": "^0.7.22",
7 "polymer": "Polymer/polymer#^1.6.1",
8 "paper-button": "PolymerElements/paper-button#^1.0.13",
9 "paper-spinner": "PolymerElements/paper-spinner#^1.2.0",
10 "paper-tooltip": "PolymerElements/paper-tooltip#^1.1.2",
11 "paper-toast": "PolymerElements/paper-toast#^1.3.0",
12 "paper-toggle-button": "PolymerElements/paper-toggle-button#^1.2.0",
13 "iron-ajax": "PolymerElements/iron-ajax#^1.4.3"
14 }
15 }
@@ -0,0 +1,60 b''
1
2 =======================
3 Dependency management
4 =======================
5
6
7 Overview
8 ========
9
10 We use the Nix package manager to handle our dependencies. In general we use the
11 packages out of the package collection `nixpkgs`. For frequently changing
12 dependencies for Python and JavaScript we use the tools which are described in
13 this section to generate the needed Nix derivations.
14
15
16 Python dependencies
17 ===================
18
19 We use the tool `pip2nix` to generate the Nix derivations for our Python
20 dependencies.
21
22 Generating the dependencies should be done with the following command:
23
24 .. code:: shell
25
26 pip2nix generate --license
27
28
29 .. note::
30
31 License extraction support is still experimental, use the version from the
32 following pull request: https://github.com/ktosiek/pip2nix/pull/30
33
34
35
36 Node dependencies
37 =================
38
39 After adding new dependencies via ``npm install --save``, use `node2nix` to
40 update the corresponding Nix derivations:
41
42 .. code:: shell
43
44 cd pkgs
45 node2nix --input ../package.json \
46 -o node-packages.nix \
47 -e node-env.nix \
48 -c node-default.nix \
49 -d --flatten
50
51
52 Bower dependencies
53 ==================
54
55 Frontend dependencies are managed based on `bower`, with `bower2nix` a tool
56 exists which can generate the needed Nix derivations:
57
58 .. code:: shell
59
60 bower2nix bower.json pkgs/bower-packages.nix
@@ -0,0 +1,78 b''
1 |RCE| 4.4.0 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2016-09-16
8
9
10 General
11 ^^^^^^^
12
13 - UI: introduced Polymer webcomponents into core application. RhodeCode will
14 be now shipped together with Polymer framework webcomponents. Most of
15 dynamic UI components that require large amounts of interaction
16 will be done now with Polymer.
17 - live-notifications: use rhodecode-toast for live notifications instead of
18 toastr jquery plugin.
19 - Svn: moved svn http support out of labs settings. It's tested and stable now.
20
21
22 New Features
23 ^^^^^^^^^^^^
24
25 - Integrations: integrations can now be configure on whole repo group to apply
26 same integrations on multiple projects/groups at once.
27 - Integrations: added scopes on integrations, scopes are: Global,
28 Repository Group (with/without children), Repositories, Root Repositories Only.
29 It will allow to configure exactly which projects use which integrations.
30 - Integrations: show branches/commits separately when posting push events
31 to hipchat/slack, fixes #4192.
32 - Pull-requests: summary page now shows update dates for pull request to
33 easier see which one were receantly updated.
34 - UI: hidden inline comments will be shown in side view when browsing the diffs
35 - Diffs: added inline comments toggle into pull requests diff view. #2884
36 - Live-chat: added summon reviewers functionality. You can now request
37 presence from online users into a chat for collaborative code-review.
38 This requires channelstream to be enabled.
39 - UX: added a static 502 page for gateway error. Once configured via
40 Nginx or Apache it will present a custom RhodeCode page while
41 backend servers are offline. Fixes #4202.
42
43
44 Security
45 ^^^^^^^^
46
47 - Passwords: forced password change will not allow users to put in the
48 old password as new one.
49
50
51 Performance
52 ^^^^^^^^^^^
53
54 - Vcs: refactor vcs-middleware to handle order of .ini file backends in
55 detection of vcs protocol. Detection ends now on first match and speeds
56 overall transaction speed.
57 - Summary: Improve the algorithm and performance of detection README files
58 inside summary page. In some cases we reduced cold-cache time from 50s to 1s.
59 - Safari: improved speed of large diffs on Safari browser.
60 - UX: remove position relative on diff td as it causes very slow
61 rendering in browsers.
62
63 Fixes
64 ^^^^^
65
66 - UX: change confirm password widget to have spacing between the fields to
67 match rest of ui, fixes: #4200.
68 - UX: show multiple tags/branches in changelog/summary instead of
69 truncating them.
70 - My-account: fix test notifications for IE10+
71 - Vcs: change way refs are retrieved for git so same name branch/tags and
72 remotes can be supported, fixes #298.
73 - Lexers: added small extensions table to extend syntax highlighting for file
74 sources. Fixes #4227.
75 - Search: fix bug where file path link was wrong when the repository name was
76 in the file path, fixes #4228
77 - Fixed INT overflow bug
78 - Events: send pushed commits always in the correct in order.
@@ -0,0 +1,186 b''
1 {
2 "dirs": {
3 "css": {
4 "src":"rhodecode/public/css",
5 "dest":"rhodecode/public/css"
6 },
7 "js": {
8 "src": "rhodecode/public/js/src",
9 "dest": "rhodecode/public/js"
10 }
11 },
12 "copy": {
13 "main": {
14 "expand": true,
15 "cwd": "bower_components",
16 "src": "webcomponentsjs/webcomponents-lite.js",
17 "dest": "<%= dirs.js.dest %>/vendors"
18 }
19 },
20 "concat": {
21 "polymercss": {
22 "src": [
23 "<%= dirs.js.src %>/components/root-styles-prefix.html",
24 "<%= dirs.css.src %>/style-polymer.css",
25 "<%= dirs.js.src %>/components/root-styles-suffix.html"
26 ],
27 "dest": "<%= dirs.js.dest %>/src/components/root-styles.gen.html",
28 "nonull": true
29 },
30 "dist": {
31 "src": [
32 "<%= dirs.js.src %>/jquery-1.11.1.min.js",
33 "<%= dirs.js.src %>/logging.js",
34 "<%= dirs.js.src %>/bootstrap.js",
35 "<%= dirs.js.src %>/mousetrap.js",
36 "<%= dirs.js.src %>/moment.js",
37 "<%= dirs.js.src %>/appenlight-client-0.4.1.min.js",
38 "<%= dirs.js.src %>/i18n_utils.js",
39 "<%= dirs.js.src %>/deform.js",
40 "<%= dirs.js.src %>/plugins/jquery.pjax.js",
41 "<%= dirs.js.src %>/plugins/jquery.dataTables.js",
42 "<%= dirs.js.src %>/plugins/flavoured_checkbox.js",
43 "<%= dirs.js.src %>/plugins/jquery.auto-grow-input.js",
44 "<%= dirs.js.src %>/plugins/jquery.autocomplete.js",
45 "<%= dirs.js.src %>/plugins/jquery.debounce.js",
46 "<%= dirs.js.src %>/plugins/jquery.mark.js",
47 "<%= dirs.js.src %>/plugins/jquery.timeago.js",
48 "<%= dirs.js.src %>/plugins/jquery.timeago-extension.js",
49 "<%= dirs.js.src %>/select2/select2.js",
50 "<%= dirs.js.src %>/codemirror/codemirror.js",
51 "<%= dirs.js.src %>/codemirror/codemirror_loadmode.js",
52 "<%= dirs.js.src %>/codemirror/codemirror_hint.js",
53 "<%= dirs.js.src %>/codemirror/codemirror_overlay.js",
54 "<%= dirs.js.src %>/codemirror/codemirror_placeholder.js",
55 "<%= dirs.js.dest %>/mode/meta.js",
56 "<%= dirs.js.dest %>/mode/meta_ext.js",
57 "<%= dirs.js.dest %>/rhodecode/i18n/select2/translations.js",
58 "<%= dirs.js.src %>/rhodecode/utils/array.js",
59 "<%= dirs.js.src %>/rhodecode/utils/string.js",
60 "<%= dirs.js.src %>/rhodecode/utils/pyroutes.js",
61 "<%= dirs.js.src %>/rhodecode/utils/ajax.js",
62 "<%= dirs.js.src %>/rhodecode/utils/autocomplete.js",
63 "<%= dirs.js.src %>/rhodecode/utils/colorgenerator.js",
64 "<%= dirs.js.src %>/rhodecode/utils/ie.js",
65 "<%= dirs.js.src %>/rhodecode/utils/os.js",
66 "<%= dirs.js.src %>/rhodecode/utils/topics.js",
67 "<%= dirs.js.src %>/rhodecode/widgets/multiselect.js",
68 "<%= dirs.js.src %>/rhodecode/init.js",
69 "<%= dirs.js.src %>/rhodecode/codemirror.js",
70 "<%= dirs.js.src %>/rhodecode/comments.js",
71 "<%= dirs.js.src %>/rhodecode/constants.js",
72 "<%= dirs.js.src %>/rhodecode/files.js",
73 "<%= dirs.js.src %>/rhodecode/followers.js",
74 "<%= dirs.js.src %>/rhodecode/menus.js",
75 "<%= dirs.js.src %>/rhodecode/notifications.js",
76 "<%= dirs.js.src %>/rhodecode/permissions.js",
77 "<%= dirs.js.src %>/rhodecode/pjax.js",
78 "<%= dirs.js.src %>/rhodecode/pullrequests.js",
79 "<%= dirs.js.src %>/rhodecode/settings.js",
80 "<%= dirs.js.src %>/rhodecode/select2_widgets.js",
81 "<%= dirs.js.src %>/rhodecode/tooltips.js",
82 "<%= dirs.js.src %>/rhodecode/users.js",
83 "<%= dirs.js.src %>/rhodecode/appenlight.js",
84 "<%= dirs.js.src %>/rhodecode.js"
85 ],
86 "dest": "<%= dirs.js.dest %>/scripts.js",
87 "nonull": true
88 }
89 },
90 "crisper": {
91 "dist": {
92 "options": {
93 "cleanup": false,
94 "onlySplit": true
95 },
96 "src": "<%= dirs.js.dest %>/rhodecode-components.html",
97 "dest": "<%= dirs.js.dest %>/rhodecode-components.js"
98 }
99 },
100 "less": {
101 "development": {
102 "options": {
103 "compress": false,
104 "yuicompress": false,
105 "optimization": 0
106 },
107 "files": {
108 "<%= dirs.css.dest %>/style.css": "<%= dirs.css.src %>/main.less",
109 "<%= dirs.css.dest %>/style-polymer.css": "<%= dirs.css.src %>/polymer.less"
110 }
111 },
112 "production": {
113 "options": {
114 "compress": true,
115 "yuicompress": true,
116 "optimization": 2
117 },
118 "files": {
119 "<%= dirs.css.dest %>/style.css": "<%= dirs.css.src %>/main.less",
120 "<%= dirs.css.dest %>/style-polymer.css": "<%= dirs.css.src %>/polymer.less"
121 }
122 },
123 "components": {
124 "files": [
125 {
126 "cwd": "<%= dirs.js.src %>/components/",
127 "dest": "<%= dirs.js.src %>/components/",
128 "src": [
129 "**/*.less"
130 ],
131 "expand": true,
132 "ext": ".css"
133 }
134 ]
135 }
136 },
137 "watch": {
138 "less": {
139 "files": [
140 "<%= dirs.css.src %>/**/*.less",
141 "<%= dirs.js.src %>/components/**/*.less"
142 ],
143 "tasks": [
144 "less:development",
145 "less:components",
146 "concat:polymercss",
147 "vulcanize"
148 ]
149 },
150 "js": {
151 "files": [
152 "!<%= dirs.js.src %>/components/root-styles.gen.html",
153 "<%= dirs.js.src %>/**/*.js",
154 "<%= dirs.js.src %>/components/**/*.html"
155 ],
156 "tasks": [
157 "less:components",
158 "concat:polymercss",
159 "vulcanize",
160 "crisper",
161 "concat:dist"
162 ]
163 }
164 },
165 "jshint": {
166 "rhodecode": {
167 "src": "<%= dirs.js.src %>/rhodecode/**/*.js",
168 "options": {
169 "jshintrc": ".jshintrc"
170 }
171 }
172 },
173 "vulcanize": {
174 "default": {
175 "options": {
176 "abspath": "",
177 "inlineScripts": true,
178 "inlineCss": true,
179 "stripComments": true
180 },
181 "files": {
182 "<%= dirs.js.dest %>/rhodecode-components.html": "<%= dirs.js.src %>/components/shared-components.html"
183 }
184 }
185 }
186 }
@@ -0,0 +1,67 b''
1 # Backported buildBowerComponents so that we can also use it with the version
2 # 16.03 which is the current stable at the time of this writing.
3 #
4 # This file can be removed once building with 16.03 is not needed anymore.
5
6 { pkgs }:
7
8 { buildInputs ? [], generated, ... } @ attrs:
9
10 let
11 bower2nix-src = pkgs.fetchzip {
12 url = "https://github.com/rvl/bower2nix/archive/v3.0.1.tar.gz";
13 sha256 = "1zbvz96k2j6g0r4lvm5cgh41a73k9dgayk7x63cmg538dzznxvyb";
14 };
15
16 bower2nix = import "${bower2nix-src}/default.nix" { inherit pkgs; };
17
18 fetchbower = import ./backport-16.03-fetchbower.nix {
19 inherit (pkgs) stdenv lib;
20 inherit bower2nix;
21 };
22
23 # Fetches the bower packages. `generated` should be the result of a
24 # `bower2nix` command.
25 bowerPackages = import generated {
26 inherit (pkgs) buildEnv;
27 inherit fetchbower;
28 };
29
30 in pkgs.stdenv.mkDerivation (
31 attrs
32 //
33 {
34 name = "bower_components-" + attrs.name;
35
36 inherit bowerPackages;
37
38 builder = builtins.toFile "builder.sh" ''
39 source $stdenv/setup
40
41 # The project's bower.json is required
42 cp $src/bower.json .
43
44 # Dereference symlinks -- bower doesn't like them
45 cp --recursive --reflink=auto \
46 --dereference --no-preserve=mode \
47 $bowerPackages bc
48
49 # Bower install in offline mode -- links together the fetched
50 # bower packages.
51 HOME=$PWD bower \
52 --config.storage.packages=bc/packages \
53 --config.storage.registry=bc/registry \
54 --offline install
55
56 # Sets up a single bower_components directory within
57 # the output derivation.
58 mkdir -p $out
59 mv bower_components $out
60 '';
61
62 buildInputs = buildInputs ++ [
63 pkgs.git
64 pkgs.nodePackages.bower
65 ];
66 }
67 )
@@ -0,0 +1,26 b''
1 { stdenv, lib, bower2nix }:
2 let
3 bowerVersion = version:
4 let
5 components = lib.splitString "#" version;
6 hash = lib.last components;
7 ver = if builtins.length components == 1 then version else hash;
8 in ver;
9
10 fetchbower = name: version: target: outputHash: stdenv.mkDerivation {
11 name = "${name}-${bowerVersion version}";
12 buildCommand = ''
13 fetch-bower --quiet --out=$PWD/out "${name}" "${target}" "${version}"
14 # In some cases, the result of fetchBower is different depending
15 # on the output directory (e.g. if the bower package contains
16 # symlinks). So use a local output directory before copying to
17 # $out.
18 cp -R out $out
19 '';
20 outputHashMode = "recursive";
21 outputHashAlgo = "sha256";
22 inherit outputHash;
23 buildInputs = [ bower2nix ];
24 };
25
26 in fetchbower
@@ -0,0 +1,31 b''
1 { fetchbower, buildEnv }:
2 buildEnv { name = "bower-env"; ignoreCollisions = true; paths = [
3 (fetchbower "webcomponentsjs" "0.7.22" "^0.7.22" "0ggh3k8ssafd056ib1m5bvzi7cpz3ry7gr5176d79na1w0c3i7dz")
4 (fetchbower "polymer" "Polymer/polymer#1.6.1" "Polymer/polymer#^1.6.1" "09mm0jgk457gvwqlc155swch7gjr6fs3g7spnvhi6vh5b6518540")
5 (fetchbower "paper-button" "PolymerElements/paper-button#1.0.13" "PolymerElements/paper-button#^1.0.13" "0i3y153nqk06pn0gk282vyybnl3g1w3w41d5i9z659cgn27g3fvm")
6 (fetchbower "paper-spinner" "PolymerElements/paper-spinner#1.2.0" "PolymerElements/paper-spinner#^1.2.0" "1av1m6y81jw3hjhz1yqy3rwcgxarjzl58ldfn4q6sn51pgzngfqb")
7 (fetchbower "paper-tooltip" "PolymerElements/paper-tooltip#1.1.2" "PolymerElements/paper-tooltip#^1.1.2" "1j64nprcyk2d2bbl3qwjyr0lbjngm4wclpyfwgai1c4y6g6bigd2")
8 (fetchbower "paper-toast" "PolymerElements/paper-toast#1.3.0" "PolymerElements/paper-toast#^1.3.0" "0x9rqxsks5455s8pk4aikpp99ijdn6kxr9gvhwh99nbcqdzcxq1m")
9 (fetchbower "paper-toggle-button" "PolymerElements/paper-toggle-button#1.2.0" "PolymerElements/paper-toggle-button#^1.2.0" "0mphcng3ngspbpg4jjn0mb91nvr4xc1phq3qswib15h6sfww1b2w")
10 (fetchbower "iron-ajax" "PolymerElements/iron-ajax#1.4.3" "PolymerElements/iron-ajax#^1.4.3" "0m3dx27arwmlcp00b7n516sc5a51f40p9vapr1nvd57l3i3z0pzm")
11 (fetchbower "iron-flex-layout" "PolymerElements/iron-flex-layout#1.3.1" "PolymerElements/iron-flex-layout#^1.0.0" "0nswv3ih3bhflgcd2wjfmddqswzgqxb2xbq65jk9w3rkj26hplbl")
12 (fetchbower "paper-behaviors" "PolymerElements/paper-behaviors#1.0.12" "PolymerElements/paper-behaviors#^1.0.0" "012bqk97awgz55cn7rm9g7cckrdhkqhls3zvp8l6nd4rdwcrdzq8")
13 (fetchbower "paper-material" "PolymerElements/paper-material#1.0.6" "PolymerElements/paper-material#^1.0.0" "0rljmknfdbm5aabvx9pk77754zckj3l127c3mvnmwkpkkr353xnh")
14 (fetchbower "paper-styles" "PolymerElements/paper-styles#1.1.4" "PolymerElements/paper-styles#^1.0.0" "0j8vg74xrcxlni8i93dsab3y80f34kk30lv4yblqpkp9c3nrilf7")
15 (fetchbower "neon-animation" "PolymerElements/neon-animation#1.2.4" "PolymerElements/neon-animation#^1.0.0" "16mz9i2n5w0k5j8d6gha23cnbdgm5syz3fawyh89gdbq97bi2q5j")
16 (fetchbower "iron-a11y-announcer" "PolymerElements/iron-a11y-announcer#1.0.5" "PolymerElements/iron-a11y-announcer#^1.0.0" "0n7c7j1pwk3835s7s2jd9125wdcsqf216yi5gj07wn5s8h8p7m9d")
17 (fetchbower "iron-overlay-behavior" "PolymerElements/iron-overlay-behavior#1.8.6" "PolymerElements/iron-overlay-behavior#^1.0.9" "14brn9gz6qqskarg3fxk91xs7vg02vgcsz9a9743kidxr0l0413m")
18 (fetchbower "iron-fit-behavior" "PolymerElements/iron-fit-behavior#1.2.5" "PolymerElements/iron-fit-behavior#^1.1.0" "1msnlh8lp1xg6v4h6dkjwj9kzac5q5q208ayla3x9hi483ki6rlf")
19 (fetchbower "iron-checked-element-behavior" "PolymerElements/iron-checked-element-behavior#1.0.5" "PolymerElements/iron-checked-element-behavior#^1.0.0" "0l0yy4ah454s8bzfv076s8by7h67zy9ni6xb932qwyhx8br6c1m7")
20 (fetchbower "promise-polyfill" "polymerlabs/promise-polyfill#1.0.1" "polymerlabs/promise-polyfill#^1.0.0" "045bj2caav3famr5hhxgs1dx7n08r4s46mlzwb313vdy17is38xb")
21 (fetchbower "iron-behaviors" "PolymerElements/iron-behaviors#1.0.17" "PolymerElements/iron-behaviors#^1.0.0" "021qvkmbk32jrrmmphpmwgby4bzi5jyf47rh1bxmq2ip07ly4bpr")
22 (fetchbower "paper-ripple" "PolymerElements/paper-ripple#1.0.8" "PolymerElements/paper-ripple#^1.0.0" "0r9sq8ik7wwrw0qb82c3rw0c030ljwd3s466c9y4qbcrsbvfjnns")
23 (fetchbower "font-roboto" "PolymerElements/font-roboto#1.0.1" "PolymerElements/font-roboto#^1.0.1" "02jz43r0wkyr3yp7rq2rc08l5cwnsgca9fr54sr4rhsnl7cjpxrj")
24 (fetchbower "iron-meta" "PolymerElements/iron-meta#1.1.2" "PolymerElements/iron-meta#^1.0.0" "1wl4dx8fnsknw9z9xi8bpc4cy9x70c11x4zxwxnj73hf3smifppl")
25 (fetchbower "iron-resizable-behavior" "PolymerElements/iron-resizable-behavior#1.0.5" "PolymerElements/iron-resizable-behavior#^1.0.0" "1fd5zmbr2hax42vmcasncvk7lzi38fmb1kyii26nn8pnnjak7zkn")
26 (fetchbower "iron-selector" "PolymerElements/iron-selector#1.5.2" "PolymerElements/iron-selector#^1.0.0" "1ajv46llqzvahm5g6g75w7nfyjcslp53ji0wm96l2k94j87spv3r")
27 (fetchbower "web-animations-js" "web-animations/web-animations-js#2.2.2" "web-animations/web-animations-js#^2.2.0" "1izfvm3l67vwys0bqbhidi9rqziw2f8wv289386sc6jsxzgkzhga")
28 (fetchbower "iron-a11y-keys-behavior" "PolymerElements/iron-a11y-keys-behavior#1.1.7" "PolymerElements/iron-a11y-keys-behavior#^1.0.0" "070z46dbbz242002gmqrgy28x0y1fcqp9hnvbi05r3zphiqfx3l7")
29 (fetchbower "iron-validatable-behavior" "PolymerElements/iron-validatable-behavior#1.1.1" "PolymerElements/iron-validatable-behavior#^1.0.0" "1yhxlvywhw2klbbgm3f3cmanxfxggagph4ii635zv0c13707wslv")
30 (fetchbower "iron-form-element-behavior" "PolymerElements/iron-form-element-behavior#1.0.6" "PolymerElements/iron-form-element-behavior#^1.0.0" "0rdhxivgkdhhz2yadgdbjfc70l555p3y83vjh8rfj5hr0asyn6q1")
31 ]; }
@@ -0,0 +1,15 b''
1 # This file has been generated by node2nix 1.0.0. Do not edit!
2
3 {pkgs ? import <nixpkgs> {
4 inherit system;
5 }, system ? builtins.currentSystem}:
6
7 let
8 nodeEnv = import ./node-env.nix {
9 inherit (pkgs) stdenv python utillinux runCommand writeTextFile nodejs;
10 };
11 in
12 import ./node-packages.nix {
13 inherit (pkgs) fetchurl fetchgit;
14 inherit nodeEnv;
15 } No newline at end of file
@@ -0,0 +1,292 b''
1 # This file originates from node2nix
2
3 {stdenv, python, nodejs, utillinux, runCommand, writeTextFile}:
4
5 let
6 # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
7 tarWrapper = runCommand "tarWrapper" {} ''
8 mkdir -p $out/bin
9
10 cat > $out/bin/tar <<EOF
11 #! ${stdenv.shell} -e
12 $(type -p tar) "\$@" --warning=no-unknown-keyword
13 EOF
14
15 chmod +x $out/bin/tar
16 '';
17
18 # Function that generates a TGZ file from a NPM project
19 buildNodeSourceDist =
20 { name, version, src, ... }:
21
22 stdenv.mkDerivation {
23 name = "node-tarball-${name}-${version}";
24 inherit src;
25 buildInputs = [ nodejs ];
26 buildPhase = ''
27 export HOME=$TMPDIR
28 tgzFile=$(npm pack)
29 '';
30 installPhase = ''
31 mkdir -p $out/tarballs
32 mv $tgzFile $out/tarballs
33 mkdir -p $out/nix-support
34 echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
35 '';
36 };
37
38 includeDependencies = {dependencies}:
39 stdenv.lib.optionalString (dependencies != [])
40 (stdenv.lib.concatMapStrings (dependency:
41 ''
42 # Bundle the dependencies of the package
43 mkdir -p node_modules
44 cd node_modules
45
46 # Only include dependencies if they don't exist. They may also be bundled in the package.
47 if [ ! -e "${dependency.name}" ]
48 then
49 ${composePackage dependency}
50 fi
51
52 cd ..
53 ''
54 ) dependencies);
55
56 # Recursively composes the dependencies of a package
57 composePackage = { name, packageName, src, dependencies ? [], ... }@args:
58 let
59 fixImpureDependencies = writeTextFile {
60 name = "fixDependencies.js";
61 text = ''
62 var fs = require('fs');
63 var url = require('url');
64
65 /*
66 * Replaces an impure version specification by *
67 */
68 function replaceImpureVersionSpec(versionSpec) {
69 var parsedUrl = url.parse(versionSpec);
70
71 if(versionSpec == "latest" || versionSpec == "unstable" ||
72 versionSpec.substr(0, 2) == ".." || dependency.substr(0, 2) == "./" || dependency.substr(0, 2) == "~/" || dependency.substr(0, 1) == '/')
73 return '*';
74 else if(parsedUrl.protocol == "git:" || parsedUrl.protocol == "git+ssh:" || parsedUrl.protocol == "git+http:" || parsedUrl.protocol == "git+https:" ||
75 parsedUrl.protocol == "http:" || parsedUrl.protocol == "https:")
76 return '*';
77 else
78 return versionSpec;
79 }
80
81 var packageObj = JSON.parse(fs.readFileSync('./package.json'));
82
83 /* Replace dependencies */
84 if(packageObj.dependencies !== undefined) {
85 for(var dependency in packageObj.dependencies) {
86 var versionSpec = packageObj.dependencies[dependency];
87 packageObj.dependencies[dependency] = replaceImpureVersionSpec(versionSpec);
88 }
89 }
90
91 /* Replace development dependencies */
92 if(packageObj.devDependencies !== undefined) {
93 for(var dependency in packageObj.devDependencies) {
94 var versionSpec = packageObj.devDependencies[dependency];
95 packageObj.devDependencies[dependency] = replaceImpureVersionSpec(versionSpec);
96 }
97 }
98
99 /* Replace optional dependencies */
100 if(packageObj.optionalDependencies !== undefined) {
101 for(var dependency in packageObj.optionalDependencies) {
102 var versionSpec = packageObj.optionalDependencies[dependency];
103 packageObj.optionalDependencies[dependency] = replaceImpureVersionSpec(versionSpec);
104 }
105 }
106
107 /* Write the fixed JSON file */
108 fs.writeFileSync("package.json", JSON.stringify(packageObj));
109 '';
110 };
111 in
112 ''
113 DIR=$(pwd)
114 cd $TMPDIR
115
116 unpackFile ${src}
117
118 # Make the base dir in which the target dependency resides first
119 mkdir -p "$(dirname "$DIR/${packageName}")"
120
121 if [ -f "${src}" ]
122 then
123 # Figure out what directory has been unpacked
124 packageDir=$(find . -type d -maxdepth 1 | tail -1)
125
126 # Restore write permissions to make building work
127 chmod -R u+w "$packageDir"
128
129 # Move the extracted tarball into the output folder
130 mv "$packageDir" "$DIR/${packageName}"
131 elif [ -d "${src}" ]
132 then
133 # Restore write permissions to make building work
134 chmod -R u+w $strippedName
135
136 # Move the extracted directory into the output folder
137 mv $strippedName "$DIR/${packageName}"
138 fi
139
140 # Unset the stripped name to not confuse the next unpack step
141 unset strippedName
142
143 # Some version specifiers (latest, unstable, URLs, file paths) force NPM to make remote connections or consult paths outside the Nix store.
144 # The following JavaScript replaces these by * to prevent that
145 cd "$DIR/${packageName}"
146 node ${fixImpureDependencies}
147
148 # Include the dependencies of the package
149 ${includeDependencies { inherit dependencies; }}
150 cd ..
151 ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
152 '';
153
154 # Extract the Node.js source code which is used to compile packages with
155 # native bindings
156 nodeSources = runCommand "node-sources" {} ''
157 tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
158 mv node-* $out
159 '';
160
161 # Builds and composes an NPM package including all its dependencies
162 buildNodePackage = { name, packageName, version, dependencies ? [], production ? true, npmFlags ? "", dontNpmInstall ? false, preRebuild ? "", ... }@args:
163
164 stdenv.lib.makeOverridable stdenv.mkDerivation (builtins.removeAttrs args [ "dependencies" ] // {
165 name = "node-${name}-${version}";
166 buildInputs = [ tarWrapper python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ args.buildInputs or [];
167 dontStrip = args.dontStrip or true; # Striping may fail a build for some package deployments
168
169 inherit dontNpmInstall preRebuild;
170
171 unpackPhase = args.unpackPhase or "true";
172
173 buildPhase = args.buildPhase or "true";
174
175 compositionScript = composePackage args;
176 passAsFile = [ "compositionScript" ];
177
178 installPhase = args.installPhase or ''
179 # Create and enter a root node_modules/ folder
180 mkdir -p $out/lib/node_modules
181 cd $out/lib/node_modules
182
183 # Compose the package and all its dependencies
184 source $compositionScriptPath
185
186 # Patch the shebangs of the bundled modules to prevent them from
187 # calling executables outside the Nix store as much as possible
188 patchShebangs .
189
190 # Deploy the Node.js package by running npm install. Since the
191 # dependencies have been provided already by ourselves, it should not
192 # attempt to install them again, which is good, because we want to make
193 # it Nix's responsibility. If it needs to install any dependencies
194 # anyway (e.g. because the dependency parameters are
195 # incomplete/incorrect), it fails.
196 #
197 # The other responsibilities of NPM are kept -- version checks, build
198 # steps, postprocessing etc.
199
200 export HOME=$TMPDIR
201 cd "${packageName}"
202 runHook preRebuild
203 npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild
204
205 if [ "$dontNpmInstall" != "1" ]
206 then
207 npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install
208 fi
209
210 # Create symlink to the deployed executable folder, if applicable
211 if [ -d "$out/lib/node_modules/.bin" ]
212 then
213 ln -s $out/lib/node_modules/.bin $out/bin
214 fi
215
216 # Create symlinks to the deployed manual page folders, if applicable
217 if [ -d "$out/lib/node_modules/${packageName}/man" ]
218 then
219 mkdir -p $out/share
220 for dir in "$out/lib/node_modules/${packageName}/man/"*
221 do
222 mkdir -p $out/share/man/$(basename "$dir")
223 for page in "$dir"/*
224 do
225 ln -s $page $out/share/man/$(basename "$dir")
226 done
227 done
228 fi
229 '';
230 });
231
232 # Builds a development shell
233 buildNodeShell = { name, packageName, version, src, dependencies ? [], production ? true, npmFlags ? "", dontNpmInstall ? false, ... }@args:
234 let
235 nodeDependencies = stdenv.mkDerivation {
236 name = "node-dependencies-${name}-${version}";
237
238 buildInputs = [ tarWrapper python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ args.buildInputs or [];
239
240 includeScript = includeDependencies { inherit dependencies; };
241 passAsFile = [ "includeScript" ];
242
243 buildCommand = ''
244 mkdir -p $out/lib
245 cd $out/lib
246 source $includeScriptPath
247
248 # Create fake package.json to make the npm commands work properly
249 cat > package.json <<EOF
250 {
251 "name": "${packageName}",
252 "version": "${version}"
253 }
254 EOF
255
256 # Patch the shebangs of the bundled modules to prevent them from
257 # calling executables outside the Nix store as much as possible
258 patchShebangs .
259
260 export HOME=$TMPDIR
261 npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild
262
263 ${stdenv.lib.optionalString (!dontNpmInstall) ''
264 npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install
265 ''}
266
267 ln -s $out/lib/node_modules/.bin $out/bin
268 '';
269 };
270 in
271 stdenv.lib.makeOverridable stdenv.mkDerivation {
272 name = "node-shell-${name}-${version}";
273
274 buildInputs = [ python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ args.buildInputs or [];
275 buildCommand = ''
276 mkdir -p $out/bin
277 cat > $out/bin/shell <<EOF
278 #! ${stdenv.shell} -e
279 $shellHook
280 exec ${stdenv.shell}
281 EOF
282 chmod +x $out/bin/shell
283 '';
284
285 # Provide the dependencies in a development shell through the NODE_PATH environment variable
286 inherit nodeDependencies;
287 shellHook = stdenv.lib.optionalString (dependencies != []) ''
288 export NODE_PATH=$nodeDependencies/lib/node_modules
289 '';
290 };
291 in
292 { inherit buildNodeSourceDist buildNodePackage buildNodeShell; }
@@ -0,0 +1,33 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 """
22 Base module for form rendering / validation - currently just a wrapper for
23 deform - later can be replaced with something custom.
24 """
25
26 from rhodecode.translation import _
27 from deform import Button, Form, widget, ValidationFailure
28
29
30 class buttons:
31 save = Button(name='Save', type='submit')
32 reset = Button(name=_('Reset'), type='reset')
33 delete = Button(name=_('Delete'), type='submit')
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,5 +1,5 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.3.1
2 current_version = 4.4.0
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:rhodecode/VERSION]
5 [bumpversion:file:rhodecode/VERSION]
@@ -8,6 +8,7 b' syntax: glob'
8 *.swp
8 *.swp
9 *.tox
9 *.tox
10 *.DS_Store*
10 *.DS_Store*
11 rhodecode/public/js/src/components/**/*.css
11
12
12 syntax: regexp
13 syntax: regexp
13
14
@@ -23,6 +24,7 b' syntax: regexp'
23 ^_dev
24 ^_dev
24 ^._dev
25 ^._dev
25 ^build/
26 ^build/
27 ^bower_components/
26 ^coverage\.xml$
28 ^coverage\.xml$
27 ^data$
29 ^data$
28 ^\.eggs/
30 ^\.eggs/
@@ -38,7 +40,11 b' syntax: regexp'
38 ^rcextensions/
40 ^rcextensions/
39 ^result$
41 ^result$
40 ^rhodecode/public/css/style.css$
42 ^rhodecode/public/css/style.css$
43 ^rhodecode/public/css/style-polymer.css$
44 ^rhodecode/public/js/rhodecode-components.html$
41 ^rhodecode/public/js/scripts.js$
45 ^rhodecode/public/js/scripts.js$
46 ^rhodecode/public/js/src/components/root-styles.gen.html$
47 ^rhodecode/public/js/vendors/webcomponentsjs/
42 ^rhodecode\.db$
48 ^rhodecode\.db$
43 ^rhodecode\.log$
49 ^rhodecode\.log$
44 ^rhodecode_dev\.log$
50 ^rhodecode_dev\.log$
@@ -4,26 +4,21 b' done = false'
4 [task:bump_version]
4 [task:bump_version]
5 done = true
5 done = true
6
6
7 [task:rc_tools_pinned]
8 done = true
9
10 [task:fixes_on_stable]
7 [task:fixes_on_stable]
11 done = true
12
8
13 [task:pip2nix_generated]
9 [task:pip2nix_generated]
14 done = true
15
10
16 [task:changelog_updated]
11 [task:changelog_updated]
17 done = true
18
12
19 [task:generate_api_docs]
13 [task:generate_api_docs]
20 done = true
14
15 [task:updated_translation]
21
16
22 [release]
17 [release]
23 state = prepared
18 state = in_progress
24 version = 4.3.1
19 version = 4.4.0
25
20
26 [task:updated_translation]
21 [task:rc_tools_pinned]
27
22
28 [task:generate_js_routes]
23 [task:generate_js_routes]
29
24
@@ -1,144 +1,15 b''
1 module.exports = function(grunt) {
1 var gruntConfig = require('./grunt_config.json');
2 grunt.initConfig({
3
4 dirs: {
5 css: "rhodecode/public/css",
6 js: {
7 "src": "rhodecode/public/js/src",
8 "dest": "rhodecode/public/js"
9 }
10 },
11
12 concat: {
13 dist: {
14 src: [
15 // Base libraries
16 '<%= dirs.js.src %>/jquery-1.11.1.min.js',
17 '<%= dirs.js.src %>/logging.js',
18 '<%= dirs.js.src %>/bootstrap.js',
19 '<%= dirs.js.src %>/mousetrap.js',
20 '<%= dirs.js.src %>/moment.js',
21 '<%= dirs.js.src %>/appenlight-client-0.4.1.min.js',
22 '<%= dirs.js.src %>/i18n_utils.js',
23 '<%= dirs.js.src %>/deform.js',
24
25 // Plugins
26 '<%= dirs.js.src %>/plugins/jquery.pjax.js',
27 '<%= dirs.js.src %>/plugins/jquery.dataTables.js',
28 '<%= dirs.js.src %>/plugins/flavoured_checkbox.js',
29 '<%= dirs.js.src %>/plugins/jquery.auto-grow-input.js',
30 '<%= dirs.js.src %>/plugins/jquery.autocomplete.js',
31 '<%= dirs.js.src %>/plugins/jquery.debounce.js',
32 '<%= dirs.js.src %>/plugins/jquery.mark.js',
33 '<%= dirs.js.src %>/plugins/jquery.timeago.js',
34 '<%= dirs.js.src %>/plugins/jquery.timeago-extension.js',
35 '<%= dirs.js.src %>/plugins/toastr.js',
36
37 // Select2
38 '<%= dirs.js.src %>/select2/select2.js',
39
40 // Code-mirror
41 '<%= dirs.js.src %>/codemirror/codemirror.js',
42 '<%= dirs.js.src %>/codemirror/codemirror_loadmode.js',
43 '<%= dirs.js.src %>/codemirror/codemirror_hint.js',
44 '<%= dirs.js.src %>/codemirror/codemirror_overlay.js',
45 '<%= dirs.js.src %>/codemirror/codemirror_placeholder.js',
46 // TODO: mikhail: this is an exception. Since the code mirror modes
47 // are loaded "on the fly", we need to keep them in a public folder
48 '<%= dirs.js.dest %>/mode/meta.js',
49 '<%= dirs.js.dest %>/mode/meta_ext.js',
50 '<%= dirs.js.dest %>/rhodecode/i18n/select2/translations.js',
51
52 // Rhodecode utilities
53 '<%= dirs.js.src %>/rhodecode/utils/array.js',
54 '<%= dirs.js.src %>/rhodecode/utils/string.js',
55 '<%= dirs.js.src %>/rhodecode/utils/pyroutes.js',
56 '<%= dirs.js.src %>/rhodecode/utils/ajax.js',
57 '<%= dirs.js.src %>/rhodecode/utils/autocomplete.js',
58 '<%= dirs.js.src %>/rhodecode/utils/colorgenerator.js',
59 '<%= dirs.js.src %>/rhodecode/utils/ie.js',
60 '<%= dirs.js.src %>/rhodecode/utils/os.js',
61 '<%= dirs.js.src %>/rhodecode/utils/topics.js',
62
63 // Rhodecode widgets
64 '<%= dirs.js.src %>/rhodecode/widgets/multiselect.js',
65
2
66 // Rhodecode components
3 module.exports = function(grunt) {
67 '<%= dirs.js.src %>/rhodecode/init.js',
4 grunt.initConfig(gruntConfig);
68 '<%= dirs.js.src %>/rhodecode/connection_controller.js',
69 '<%= dirs.js.src %>/rhodecode/codemirror.js',
70 '<%= dirs.js.src %>/rhodecode/comments.js',
71 '<%= dirs.js.src %>/rhodecode/constants.js',
72 '<%= dirs.js.src %>/rhodecode/files.js',
73 '<%= dirs.js.src %>/rhodecode/followers.js',
74 '<%= dirs.js.src %>/rhodecode/menus.js',
75 '<%= dirs.js.src %>/rhodecode/notifications.js',
76 '<%= dirs.js.src %>/rhodecode/permissions.js',
77 '<%= dirs.js.src %>/rhodecode/pjax.js',
78 '<%= dirs.js.src %>/rhodecode/pullrequests.js',
79 '<%= dirs.js.src %>/rhodecode/settings.js',
80 '<%= dirs.js.src %>/rhodecode/select2_widgets.js',
81 '<%= dirs.js.src %>/rhodecode/tooltips.js',
82 '<%= dirs.js.src %>/rhodecode/users.js',
83 '<%= dirs.js.src %>/rhodecode/utils/notifications.js',
84 '<%= dirs.js.src %>/rhodecode/appenlight.js',
85
86 // Rhodecode main module
87 '<%= dirs.js.src %>/rhodecode.js'
88 ],
89 dest: '<%= dirs.js.dest %>/scripts.js',
90 nonull: true
91 }
92 },
93
94 less: {
95 development: {
96 options: {
97 compress: false,
98 yuicompress: false,
99 optimization: 0
100 },
101 files: {
102 "<%= dirs.css %>/style.css": "<%= dirs.css %>/main.less"
103 }
104 },
105 production: {
106 options: {
107 compress: true,
108 yuicompress: true,
109 optimization: 2
110 },
111 files: {
112 "<%= dirs.css %>/style.css": "<%= dirs.css %>/main.less"
113 }
114 }
115 },
116
117 watch: {
118 less: {
119 files: ["<%= dirs.css %>/*.less"],
120 tasks: ["less:production"]
121 },
122 js: {
123 files: ["<%= dirs.js.src %>/**/*.js"],
124 tasks: ["concat:dist"]
125 }
126 },
127
128 jshint: {
129 rhodecode: {
130 src: '<%= dirs.js.src %>/rhodecode/**/*.js',
131 options: {
132 jshintrc: '.jshintrc'
133 }
134 }
135 }
136 });
137
5
138 grunt.loadNpmTasks('grunt-contrib-less');
6 grunt.loadNpmTasks('grunt-contrib-less');
139 grunt.loadNpmTasks('grunt-contrib-concat');
7 grunt.loadNpmTasks('grunt-contrib-concat');
140 grunt.loadNpmTasks('grunt-contrib-watch');
8 grunt.loadNpmTasks('grunt-contrib-watch');
141 grunt.loadNpmTasks('grunt-contrib-jshint');
9 grunt.loadNpmTasks('grunt-contrib-jshint');
10 grunt.loadNpmTasks('grunt-vulcanize');
11 grunt.loadNpmTasks('grunt-crisper');
12 grunt.loadNpmTasks('grunt-contrib-copy');
142
13
143 grunt.registerTask('default', ['less:production', 'concat:dist']);
14 grunt.registerTask('default', ['less:production', 'less:components', 'concat:polymercss', 'copy','vulcanize', 'crisper', 'concat:dist']);
144 };
15 };
@@ -29,6 +29,9 b' recursive-include rhodecode *.mako'
29 # 502 page
29 # 502 page
30 include rhodecode/public/502.html
30 include rhodecode/public/502.html
31
31
32 # 502 page
33 include rhodecode/public/502.html
34
32 # images, css
35 # images, css
33 include rhodecode/public/css/*.css
36 include rhodecode/public/css/*.css
34 include rhodecode/public/images/*.*
37 include rhodecode/public/images/*.*
@@ -414,7 +414,7 b' search.location = %(here)s/data/index'
414 ## channelstream enables persistent connections and live notification
414 ## channelstream enables persistent connections and live notification
415 ## in the system. It's also used by the chat system
415 ## in the system. It's also used by the chat system
416
416
417 channelstream.enabled = true
417 channelstream.enabled = false
418 ## location of channelstream server on the backend
418 ## location of channelstream server on the backend
419 channelstream.server = 127.0.0.1:9800
419 channelstream.server = 127.0.0.1:9800
420 ## location of the channelstream server from outside world
420 ## location of the channelstream server from outside world
@@ -388,7 +388,7 b' search.location = %(here)s/data/index'
388 ## channelstream enables persistent connections and live notification
388 ## channelstream enables persistent connections and live notification
389 ## in the system. It's also used by the chat system
389 ## in the system. It's also used by the chat system
390
390
391 channelstream.enabled = true
391 channelstream.enabled = false
392 ## location of channelstream server on the backend
392 ## location of channelstream server on the backend
393 channelstream.server = 127.0.0.1:9800
393 channelstream.server = 127.0.0.1:9800
394 ## location of the channelstream server from outside world
394 ## location of the channelstream server from outside world
@@ -30,6 +30,10 b' let'
30 then pythonPackages
30 then pythonPackages
31 else getAttr pythonPackages pkgs;
31 else getAttr pythonPackages pkgs;
32
32
33 buildBowerComponents =
34 pkgs.buildBowerComponents or
35 (import ./pkgs/backport-16.03-build-bower-components.nix { inherit pkgs; });
36
33 elem = builtins.elem;
37 elem = builtins.elem;
34 basename = path: with pkgs.lib; last (splitString "/" path);
38 basename = path: with pkgs.lib; last (splitString "/" path);
35 startsWith = prefix: full: let
39 startsWith = prefix: full: let
@@ -41,31 +45,28 b' let'
41 ext = last (splitString "." path);
45 ext = last (splitString "." path);
42 in
46 in
43 !elem (basename path) [
47 !elem (basename path) [
44 ".git" ".hg" "__pycache__" ".eggs" "node_modules"
48 ".git" ".hg" "__pycache__" ".eggs"
45 "build" "data" "tmp"] &&
49 "bower_components" "node_modules"
50 "build" "data" "result" "tmp"] &&
46 !elem ext ["egg-info" "pyc"] &&
51 !elem ext ["egg-info" "pyc"] &&
52 # TODO: johbo: This check is wrong, since "path" contains an absolute path,
53 # it would still be good to restore it since we want to ignore "result-*".
47 !startsWith "result" path;
54 !startsWith "result" path;
48
55
49 sources = pkgs.config.rc.sources or {};
56 sources = pkgs.config.rc.sources or {};
57 version = builtins.readFile ./rhodecode/VERSION;
50 rhodecode-enterprise-ce-src = builtins.filterSource src-filter ./.;
58 rhodecode-enterprise-ce-src = builtins.filterSource src-filter ./.;
51
59
52 # Load the generated node packages
60 nodeEnv = import ./pkgs/node-default.nix {
53 nodePackages = pkgs.callPackage "${pkgs.path}/pkgs/top-level/node-packages.nix" rec {
61 inherit pkgs;
54 self = nodePackages;
55 generated = pkgs.callPackage ./pkgs/node-packages.nix { inherit self; };
56 };
62 };
63 nodeDependencies = nodeEnv.shell.nodeDependencies;
57
64
58 # TODO: Should be taken automatically out of the generates packages.
65 bowerComponents = buildBowerComponents {
59 # apps.nix has one solution for this, although I'd prefer to have the deps
66 name = "enterprise-ce-${version}";
60 # from package.json mapped in here.
67 generated = ./pkgs/bower-packages.nix;
61 nodeDependencies = with nodePackages; [
68 src = rhodecode-enterprise-ce-src;
62 grunt
69 };
63 grunt-contrib-concat
64 grunt-contrib-jshint
65 grunt-contrib-less
66 grunt-contrib-watch
67 jshint
68 ];
69
70
70 pythonGeneratedPackages = self: basePythonPackages.override (a: {
71 pythonGeneratedPackages = self: basePythonPackages.override (a: {
71 inherit self;
72 inherit self;
@@ -86,16 +87,25 b' let'
86 pythonLocalOverrides = self: super: {
87 pythonLocalOverrides = self: super: {
87 rhodecode-enterprise-ce =
88 rhodecode-enterprise-ce =
88 let
89 let
89 version = builtins.readFile ./rhodecode/VERSION;
90 linkNodeAndBowerPackages = ''
90 linkNodeModules = ''
91 echo "Export RhodeCode CE path"
92 export RHODECODE_CE_PATH=${rhodecode-enterprise-ce-src}
91 echo "Link node packages"
93 echo "Link node packages"
92 # TODO: check if this adds stuff as a dependency, closure size
93 rm -fr node_modules
94 rm -fr node_modules
94 mkdir -p node_modules
95 mkdir node_modules
95 ${pkgs.lib.concatMapStrings (dep: ''
96 # johbo: Linking individual packages allows us to run "npm install"
96 ln -sfv ${dep}/lib/node_modules/${dep.pkgName} node_modules/
97 # inside of a shell to try things out. Re-entering the shell will
97 '') nodeDependencies}
98 # restore a clean environment.
99 ln -s ${nodeDependencies}/lib/node_modules/* node_modules/
100
98 echo "DONE: Link node packages"
101 echo "DONE: Link node packages"
102
103 echo "Link bower packages"
104 rm -fr bower_components
105 mkdir bower_components
106
107 ln -s ${bowerComponents}/bower_components/* bower_components/
108 echo "DONE: Link bower packages"
99 '';
109 '';
100 in super.rhodecode-enterprise-ce.override (attrs: {
110 in super.rhodecode-enterprise-ce.override (attrs: {
101
111
@@ -109,6 +119,7 b' let'
109 buildInputs =
119 buildInputs =
110 attrs.buildInputs ++
120 attrs.buildInputs ++
111 (with self; [
121 (with self; [
122 pkgs.nodePackages.bower
112 pkgs.nodePackages.grunt-cli
123 pkgs.nodePackages.grunt-cli
113 pkgs.subversion
124 pkgs.subversion
114 pytest-catchlog
125 pytest-catchlog
@@ -123,7 +134,8 b' let'
123 # pkgs/default.nix?
134 # pkgs/default.nix?
124 passthru = {
135 passthru = {
125 inherit
136 inherit
126 linkNodeModules
137 bowerComponents
138 linkNodeAndBowerPackages
127 myPythonPackagesUnfix
139 myPythonPackagesUnfix
128 pythonLocalOverrides;
140 pythonLocalOverrides;
129 pythonPackages = self;
141 pythonPackages = self;
@@ -145,7 +157,7 b' let'
145 export PYTHONPATH="$tmp_path/${self.python.sitePackages}:$PYTHONPATH"
157 export PYTHONPATH="$tmp_path/${self.python.sitePackages}:$PYTHONPATH"
146 mkdir -p $tmp_path/${self.python.sitePackages}
158 mkdir -p $tmp_path/${self.python.sitePackages}
147 python setup.py develop --prefix $tmp_path --allow-hosts ""
159 python setup.py develop --prefix $tmp_path --allow-hosts ""
148 '' + linkNodeModules;
160 '' + linkNodeAndBowerPackages;
149
161
150 preCheck = ''
162 preCheck = ''
151 export PATH="$out/bin:$PATH"
163 export PATH="$out/bin:$PATH"
@@ -156,7 +168,7 b' let'
156 rm -rf $out/lib/${self.python.libPrefix}/site-packages/rhodecode/tests
168 rm -rf $out/lib/${self.python.libPrefix}/site-packages/rhodecode/tests
157 '';
169 '';
158
170
159 preBuild = linkNodeModules + ''
171 preBuild = linkNodeAndBowerPackages + ''
160 grunt
172 grunt
161 rm -fr node_modules
173 rm -fr node_modules
162 '';
174 '';
@@ -29,13 +29,3 b' 4. You will see the labs setting on the'
29 :menuselection:`Admin --> Settings --> labs` page.
29 :menuselection:`Admin --> Settings --> labs` page.
30
30
31 .. image:: ../images/lab-setting.png
31 .. image:: ../images/lab-setting.png
32
33 Available Lab Extras
34 --------------------
35
36 Once lab settings are enabled, the following features are available.
37
38 .. toctree::
39 :maxdepth: 1
40
41 svn-http
@@ -26,6 +26,7 b' For more information, see the following '
26 * :ref:`vcs-server-versions`
26 * :ref:`vcs-server-versions`
27 * :ref:`vcs-server-maintain`
27 * :ref:`vcs-server-maintain`
28 * :ref:`vcs-server-config-file`
28 * :ref:`vcs-server-config-file`
29 * :ref:`svn-http`
29
30
30 .. _install-vcs:
31 .. _install-vcs:
31
32
@@ -297,5 +298,133 b' For a more detailed explanation of the l'
297 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
298 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
298 datefmt = %Y-%m-%d %H:%M:%S
299 datefmt = %Y-%m-%d %H:%M:%S
299
300
301 .. _svn-http:
300
302
301 .. _Ask Ubuntu: http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue
303 |svn| With Write Over HTTP
304 ^^^^^^^^^^^^^^^^^^^^^^^^^^
305
306 To use |svn| with read/write support over the |svn| HTTP protocol, you have to
307 configure the HTTP |svn| backend.
308
309 Prerequisites
310 =============
311
312 - Enable HTTP support inside the admin VCS settings on your |RCE| instance
313 - You need to install the following tools on the machine that is running an
314 instance of |RCE|:
315 ``Apache HTTP Server`` and
316 ``mod_dav_svn``.
317
318
319 Using Ubuntu Distribution as an example you can run:
320
321 .. code-block:: bash
322
323 $ sudo apt-get install apache2 libapache2-mod-svn
324
325 Once installed you need to enable ``dav_svn``:
326
327 .. code-block:: bash
328
329 $ sudo a2enmod dav_svn
330
331 Configuring Apache Setup
332 ========================
333
334 .. tip::
335
336 It is recommended to run Apache on a port other than 80, due to possible
337 conflicts with other HTTP servers like nginx. To do this, set the
338 ``Listen`` parameter in the ``/etc/apache2/ports.conf`` file, for example
339 ``Listen 8090``.
340
341
342 .. warning::
343
344 Make sure your Apache instance which runs the mod_dav_svn module is
345 only accessible by RhodeCode. Otherwise everyone is able to browse
346 the repositories or run subversion operations (checkout/commit/etc.).
347
348 It is also recommended to run apache as the same user as |RCE|, otherwise
349 permission issues could occur. To do this edit the ``/etc/apache2/envvars``
350
351 .. code-block:: apache
352
353 export APACHE_RUN_USER=rhodecode
354 export APACHE_RUN_GROUP=rhodecode
355
356 1. To configure Apache, create and edit a virtual hosts file, for example
357 :file:`/etc/apache2/sites-available/default.conf`. Below is an example
358 how to use one with auto-generated config ```mod_dav_svn.conf```
359 from configured |RCE| instance.
360
361 .. code-block:: apache
362
363 <VirtualHost *:8080>
364 ServerAdmin rhodecode-admin@localhost
365 DocumentRoot /var/www/html
366 ErrorLog ${'${APACHE_LOG_DIR}'}/error.log
367 CustomLog ${'${APACHE_LOG_DIR}'}/access.log combined
368 Include /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
369 </VirtualHost>
370
371
372 2. Go to the :menuselection:`Admin --> Settings --> VCS` page, and
373 enable :guilabel:`Proxy Subversion HTTP requests`, and specify the
374 :guilabel:`Subversion HTTP Server URL`.
375
376 3. Open the |RCE| configuration file,
377 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
378
379 4. Add the following configuration option in the ``[app:main]``
380 section if you don't have it yet.
381
382 This enables mapping of the created |RCE| repo groups into special |svn| paths.
383 Each time a new repository group is created, the system will update
384 the template file and create new mapping. Apache web server needs to be
385 reloaded to pick up the changes on this file.
386 It's recommended to add reload into a crontab so the changes can be picked
387 automatically once someone creates a repository group inside RhodeCode.
388
389
390 .. code-block:: ini
391
392 ##############################################
393 ### Subversion proxy support (mod_dav_svn) ###
394 ##############################################
395 ## Enable or disable the config file generation.
396 svn.proxy.generate_config = true
397 ## Generate config file with `SVNListParentPath` set to `On`.
398 svn.proxy.list_parent_path = true
399 ## Set location and file name of generated config file.
400 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
401 ## File system path to the directory containing the repositories served by
402 ## RhodeCode.
403 svn.proxy.parent_path_root = /path/to/repo_store
404 ## Used as a prefix to the <Location> block in the generated config file. In
405 ## most cases it should be set to `/`.
406 svn.proxy.location_root = /
407
408
409 This would create a special template file called ```mod_dav_svn.conf```. We
410 used that file path in the apache config above inside the Include statement.
411
412
413 Using |svn|
414 ===========
415
416 Once |svn| has been enabled on your instance, you can use it with the
417 following examples. For more |svn| information, see the `Subversion Red Book`_
418
419 .. code-block:: bash
420
421 # To clone a repository
422 svn checkout http://my-svn-server.example.com/my-svn-repo
423
424 # svn commit
425 svn commit
426
427 .. _Subversion Red Book: http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.ref.svn
428
429
430 .. _Ask Ubuntu: http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue No newline at end of file
@@ -18,3 +18,4 b' Welcome to the contribution guides and d'
18 db-schema
18 db-schema
19 dev-settings
19 dev-settings
20 api
20 api
21 dependencies
@@ -111,15 +111,18 b' time operation::'
111 Compile CSS and JavaScript
111 Compile CSS and JavaScript
112 ^^^^^^^^^^^^^^^^^^^^^^^^^^
112 ^^^^^^^^^^^^^^^^^^^^^^^^^^
113
113
114 To use the application's frontend, you will need to compile the CSS and
114 To use the application's frontend and prepare it for production deployment,
115 JavaScript with Grunt. This is easily done from within the nix-shell using the
115 you will need to compile the CSS and JavaScript with Grunt.
116 following command::
116 This is easily done from within the nix-shell using the following command::
117
118 grunt
117
119
118 make web-build
120 When developing new features you will need to recompile following any
121 changes made to the CSS or JavaScript files when developing the code::
119
122
120 You will need to recompile following any changes made to the CSS or JavaScript
123 grunt watch
121 files.
122
124
125 This prepares the development (with comments/whitespace) versions of files.
123
126
124 Start the Development Server
127 Start the Development Server
125 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
128 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,6 +45,11 b' JavaScript'
45 ----------
45 ----------
46 This currently remains undefined. Suggestions welcome!
46 This currently remains undefined. Suggestions welcome!
47
47
48 However, we have decided to go forward with W3C standards and picked
49 WebComponents as the foundation of user interface. New functionality should
50 be implemented as components using the
51 `Polymer Project` <https://www.polymer-project.org>`_ library
52 and should avoid external dependencies like `jquery`.
48
53
49 HTML
54 HTML
50 ----
55 ----
@@ -14,9 +14,6 b' py.test based test suite'
14 The test suite is in the folder :file:`rhodecode/tests/` and should be run with
14 The test suite is in the folder :file:`rhodecode/tests/` and should be run with
15 the test runner `py.test` inside of your `nix-shell` environment::
15 the test runner `py.test` inside of your `nix-shell` environment::
16
16
17 # In case you need the cythonized version
18 CYTHONIZE=1 python setup.py develop --prefix=$tmp_path
19
20 py.test rhodecode
17 py.test rhodecode
21
18
22
19
@@ -26,20 +23,28 b' py.test integration'
26
23
27 The integration with the test runner is based on the following three parts:
24 The integration with the test runner is based on the following three parts:
28
25
29 - `pytest_pylons` is a py.test plugin which does the integration with the
26 - :file:`rhodecode/tests/pylons_plugin.py` is a py.test plugin which does the
30 Pylons web framework. It sets up the Pylons environment based on the given ini
27 integration with the Pylons web framework. It sets up the Pylons environment
31 file.
28 based on the given ini file.
32
29
33 Tests which depend on the Pylons environment to be set up must request the
30 Tests which depend on the Pylons environment to be set up must request the
34 fixture `pylonsapp`.
31 fixture `pylonsapp`.
35
32
36 - :file:`rhodecode/tests/plugin.py` contains the integration of py.test with
33 - :file:`rhodecode/tests/plugin.py` contains the integration of py.test with
37 RhodeCode Enterprise itself.
34 RhodeCode Enterprise itself and it takes care of setting up the needed parts
35 of the Pyramid framework.
38
36
39 - :file:`conftest.py` plugins are used to provide a special integration for
37 - :file:`conftest.py` plugins are used to provide a special integration for
40 certain groups of tests based on the directory location.
38 certain groups of tests based on the directory location.
41
39
42
40
41 .. note::
42
43 We are migrating from Pylons to its successor Pyramid. Eventually the role of
44 the file `pylons_plugin.py` will change to provide only a Pyramid
45 integration.
46
47
43
48
44 VCS backend selection
49 VCS backend selection
45 ---------------------
50 ---------------------
@@ -19,8 +19,7 b' Quick Start Installation Guide'
19
19
20 To get |RCE| up and running, run through the below steps:
20 To get |RCE| up and running, run through the below steps:
21
21
22 1. Download the latest |RCC| installer from your `rhodecode.com`_ profile
22 1. Download the latest |RCC| installer from `rhodecode.com/download`_.
23 or main page.
24 If you don't have an account, sign up at `rhodecode.com/register`_.
23 If you don't have an account, sign up at `rhodecode.com/register`_.
25
24
26 2. Run the |RCC| installer and accept the End User Licence using the
25 2. Run the |RCC| installer and accept the End User Licence using the
@@ -107,3 +106,5 b' 5. Check the status of your installation'
107 .. _rhodecode.com/download/: https://rhodecode.com/download/
106 .. _rhodecode.com/download/: https://rhodecode.com/download/
108 .. _rhodecode.com: https://rhodecode.com/
107 .. _rhodecode.com: https://rhodecode.com/
109 .. _rhodecode.com/register: https://rhodecode.com/register/
108 .. _rhodecode.com/register: https://rhodecode.com/register/
109 .. _rhodecode.com/download: https://rhodecode.com/download/
110
@@ -3,20 +3,20 b''
3 PostgreSQL
3 PostgreSQL
4 ----------
4 ----------
5
5
6 To use a PostgreSQL database you should install and configurevthe database
6 To use a PostgreSQL database, you should install and configure the database
7 before installing |RCV|. This is becausevduring |RCV| installation you will
7 before installing |RCV|. This is because during |RCV| installation you will
8 setup a connection to your PostgreSQL database. To work with PostgreSQL,
8 setup the connection to your PostgreSQL database. To work with PostgreSQL,
9 use the following steps:
9 use the following steps:
10
10
11 1. Depending on your |os|, install avPostgreSQL database following the
11 1. Depending on your |os|, install a PostgreSQL database following the
12 appropriate instructions from the `PostgreSQL website`_.
12 appropriate instructions from the `PostgreSQL website`_.
13 2. Configure the database with a username and password which you will use
13 2. Configure the database with a username and password, which you will use
14 with |RCV|.
14 with |RCV|.
15 3. Install |RCV|, and during installation select PostgreSQL as your database.
15 3. Install |RCV|, and during installation select PostgreSQL as your database.
16 4. Enter the following information to during the database setup:
16 4. Enter the following information during the database setup:
17
17
18 * Your network IP Address
18 * Your network IP Address
19 * The port number for MySQL access. The default MySQL port is ``5434``
19 * The port number for PostgreSQL access; the default port is ``5434``
20 * Your database username
20 * Your database username
21 * Your database password
21 * Your database password
22 * A new database name
22 * A new database name
@@ -9,6 +9,7 b' Release Notes'
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.4.0.rst
12 release-notes-4.3.1.rst
13 release-notes-4.3.1.rst
13 release-notes-4.3.0.rst
14 release-notes-4.3.0.rst
14 release-notes-4.2.1.rst
15 release-notes-4.2.1.rst
@@ -3,10 +3,16 b''
3 "version": "0.0.1",
3 "version": "0.0.1",
4 "devDependencies": {
4 "devDependencies": {
5 "grunt": "^0.4.5",
5 "grunt": "^0.4.5",
6 "grunt-contrib-copy": "^1.0.0",
6 "grunt-contrib-concat": "^0.5.1",
7 "grunt-contrib-concat": "^0.5.1",
7 "grunt-contrib-jshint": "^0.12.0",
8 "grunt-contrib-jshint": "^0.12.0",
8 "grunt-contrib-less": "^1.1.0",
9 "grunt-contrib-less": "^1.1.0",
9 "grunt-contrib-watch": "^0.6.1",
10 "grunt-contrib-watch": "^0.6.1",
10 "jshint": "^2.9.1-rc3"
11 "crisper": "^2.0.2",
12 "vulcanize": "^1.14.8",
13 "grunt-crisper": "^1.0.1",
14 "grunt-vulcanize": "^1.0.0",
15 "jshint": "^2.9.1-rc3",
16 "bower": "^1.7.9"
11 }
17 }
12 }
18 }
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -14,6 +14,20 b' let'
14 url = http://www.repoze.org/LICENSE.txt;
14 url = http://www.repoze.org/LICENSE.txt;
15 };
15 };
16 };
16 };
17
18 # johbo: Interim bridge which allows us to build with the upcoming
19 # nixos.16.09 branch (unstable at the moment of writing this note) and the
20 # current stable nixos-16.03.
21 backwardsCompatibleFetchgit = { ... }@args:
22 let
23 origSources = pkgs.fetchgit args;
24 in
25 pkgs.lib.overrideDerivation origSources (oldAttrs: {
26 NIX_PREFETCH_GIT_CHECKOUT_HOOK = ''
27 find $out -name '.git*' -print0 | xargs -0 rm -rf
28 '';
29 });
30
17 in
31 in
18
32
19 self: super: {
33 self: super: {
@@ -96,7 +110,7 b' self: super: {'
96 });
110 });
97
111
98 py-gfm = super.py-gfm.override {
112 py-gfm = super.py-gfm.override {
99 src = pkgs.fetchgit {
113 src = backwardsCompatibleFetchgit {
100 url = "https://code.rhodecode.com/upstream/py-gfm";
114 url = "https://code.rhodecode.com/upstream/py-gfm";
101 rev = "0d66a19bc16e3d49de273c0f797d4e4781e8c0f2";
115 rev = "0d66a19bc16e3d49de273c0f797d4e4781e8c0f2";
102 sha256 = "0ryp74jyihd3ckszq31bml5jr3bciimhfp7va7kw6ld92930ksv3";
116 sha256 = "0ryp74jyihd3ckszq31bml5jr3bciimhfp7va7kw6ld92930ksv3";
@@ -120,7 +134,7 b' self: super: {'
120
134
121 Pylons = super.Pylons.override (attrs: {
135 Pylons = super.Pylons.override (attrs: {
122 name = "Pylons-1.0.1-patch1";
136 name = "Pylons-1.0.1-patch1";
123 src = pkgs.fetchgit {
137 src = backwardsCompatibleFetchgit {
124 url = "https://code.rhodecode.com/upstream/pylons";
138 url = "https://code.rhodecode.com/upstream/pylons";
125 rev = "707354ee4261b9c10450404fc9852ccea4fd667d";
139 rev = "707354ee4261b9c10450404fc9852ccea4fd667d";
126 sha256 = "b2763274c2780523a335f83a1df65be22ebe4ff413a7bc9e9288d23c1f62032e";
140 sha256 = "b2763274c2780523a335f83a1df65be22ebe4ff413a7bc9e9288d23c1f62032e";
@@ -51,19 +51,6 b''
51 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
51 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
52 };
52 };
53 };
53 };
54 Fabric = super.buildPythonPackage {
55 name = "Fabric-1.10.0";
56 buildInputs = with self; [];
57 doCheck = false;
58 propagatedBuildInputs = with self; [paramiko];
59 src = fetchurl {
60 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
61 md5 = "2cb96473387f0e7aa035210892352f4a";
62 };
63 meta = {
64 license = [ pkgs.lib.licenses.bsdOriginal ];
65 };
66 };
67 FormEncode = super.buildPythonPackage {
54 FormEncode = super.buildPythonPackage {
68 name = "FormEncode-1.2.4";
55 name = "FormEncode-1.2.4";
69 buildInputs = with self; [];
56 buildInputs = with self; [];
@@ -1430,7 +1417,7 b''
1430 };
1417 };
1431 };
1418 };
1432 rhodecode-enterprise-ce = super.buildPythonPackage {
1419 rhodecode-enterprise-ce = super.buildPythonPackage {
1433 name = "rhodecode-enterprise-ce-4.3.1";
1420 name = "rhodecode-enterprise-ce-4.4.0";
1434 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1421 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1435 doCheck = true;
1422 doCheck = true;
1436 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery channelstream colander decorator deform docutils gevent gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
1423 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery channelstream colander decorator deform docutils gevent gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
@@ -1,7 +1,6 b''
1 Babel==1.3
1 Babel==1.3
2 Beaker==1.7.0
2 Beaker==1.7.0
3 CProfileV==1.0.6
3 CProfileV==1.0.6
4 Fabric==1.10.0
5 FormEncode==1.2.4
4 FormEncode==1.2.4
6 Jinja2==2.7.3
5 Jinja2==2.7.3
7 Mako==1.0.1
6 Mako==1.0.1
@@ -1,1 +1,1 b''
1 4.3.1 No newline at end of file
1 4.4.0 No newline at end of file
@@ -51,7 +51,7 b' PYRAMID_SETTINGS = {}'
51 EXTENSIONS = {}
51 EXTENSIONS = {}
52
52
53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 55 # defines current db version for migrations
54 __dbversion__ = 58 # defines current db version for migrations
55 __platform__ = platform.system()
55 __platform__ = platform.system()
56 __license__ = 'AGPLv3, and Commercial License'
56 __license__ = 'AGPLv3, and Commercial License'
57 __author__ = 'RhodeCode GmbH'
57 __author__ = 'RhodeCode GmbH'
@@ -60,3 +60,4 b' EXTENSIONS = {}'
60 is_windows = __platform__ in ['Windows']
60 is_windows = __platform__ in ['Windows']
61 is_unix = not is_windows
61 is_unix = not is_windows
62 is_test = False
62 is_test = False
63 disable_error_handler = False
@@ -29,7 +29,9 b' from rhodecode.lib.ext_json import json'
29 def url_gen(request):
29 def url_gen(request):
30 urls = {
30 urls = {
31 'connect': request.route_url('channelstream_connect'),
31 'connect': request.route_url('channelstream_connect'),
32 'subscribe': request.route_url('channelstream_subscribe')
32 'subscribe': request.route_url('channelstream_subscribe'),
33 'longpoll': request.registry.settings.get('channelstream.longpoll_url', ''),
34 'ws': request.registry.settings.get('channelstream.ws_url', '')
33 }
35 }
34 return json.dumps(urls)
36 return json.dumps(urls)
35
37
@@ -95,6 +95,7 b' class ChannelstreamView(object):'
95 'display_name': None,
95 'display_name': None,
96 'display_link': None,
96 'display_link': None,
97 }
97 }
98 user_data['permissions'] = c.rhodecode_user.permissions
98 payload = {
99 payload = {
99 'username': user.username,
100 'username': user.username,
100 'user_state': user_data,
101 'user_state': user_data,
@@ -28,7 +28,11 b' from rhodecode.lib.utils2 import __get_l'
28
28
29 # language map is also used by whoosh indexer, which for those specified
29 # language map is also used by whoosh indexer, which for those specified
30 # extensions will index it's content
30 # extensions will index it's content
31 LANGUAGES_EXTENSIONS_MAP = __get_lem()
31 # custom extensions to lexers, format is 'ext': 'LexerClass'
32 extra = {
33 'vbs': 'VbNet'
34 }
35 LANGUAGES_EXTENSIONS_MAP = __get_lem(extra)
32
36
33 DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
37 DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
34
38
@@ -158,6 +158,8 b' def load_pyramid_environment(global_conf'
158 # This has to be done before the database connection is initialized.
158 # This has to be done before the database connection is initialized.
159 if settings['is_test']:
159 if settings['is_test']:
160 rhodecode.is_test = True
160 rhodecode.is_test = True
161 rhodecode.disable_error_handler = True
162
161 utils.initialize_test_environment(settings_merged)
163 utils.initialize_test_environment(settings_merged)
162
164
163 # Initialize the database connection.
165 # Initialize the database connection.
@@ -44,9 +44,10 b' from rhodecode.config import patches'
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.vcs.exceptions import VCSCommunicationError
47 from rhodecode.lib.middleware import csrf
49 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
@@ -193,10 +194,6 b' def make_not_found_view(config):'
193
194
194 pylons_app_as_view = wsgiapp(pylons_app)
195 pylons_app_as_view = wsgiapp(pylons_app)
195
196
196 # Protect from VCS Server error related pages when server is not available
197 if not vcs_server_enabled:
198 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
199
200 def pylons_app_with_error_handler(context, request):
197 def pylons_app_with_error_handler(context, request):
201 """
198 """
202 Handle exceptions from rc pylons app:
199 Handle exceptions from rc pylons app:
@@ -221,10 +218,18 b' def make_not_found_view(config):'
221 return error_handler(response, request)
218 return error_handler(response, request)
222 except HTTPError as e: # pyramid type exceptions
219 except HTTPError as e: # pyramid type exceptions
223 return error_handler(e, request)
220 return error_handler(e, request)
224 except Exception:
221 except Exception as e:
225 if settings.get('debugtoolbar.enabled', False):
222 log.exception(e)
223
224 if (settings.get('debugtoolbar.enabled', False) or
225 rhodecode.disable_error_handler):
226 raise
226 raise
227
228 if isinstance(e, VCSCommunicationError):
229 return error_handler(VCSServerUnavailable(), request)
230
227 return error_handler(HTTPInternalServerError(), request)
231 return error_handler(HTTPInternalServerError(), request)
232
228 return response
233 return response
229
234
230 return pylons_app_with_error_handler
235 return pylons_app_with_error_handler
@@ -249,7 +254,6 b' def webob_to_pyramid_http_response(webob'
249
254
250
255
251 def error_handler(exception, request):
256 def error_handler(exception, request):
252 # TODO: dan: replace the old pylons error controller with this
253 from rhodecode.model.settings import SettingsModel
257 from rhodecode.model.settings import SettingsModel
254 from rhodecode.lib.utils2 import AttributeDict
258 from rhodecode.lib.utils2 import AttributeDict
255
259
@@ -278,6 +282,10 b' def error_handler(exception, request):'
278 if not c.rhodecode_name:
282 if not c.rhodecode_name:
279 c.rhodecode_name = 'Rhodecode'
283 c.rhodecode_name = 'Rhodecode'
280
284
285 c.causes = []
286 if hasattr(base_response, 'causes'):
287 c.causes = base_response.causes
288
281 response = render_to_response(
289 response = render_to_response(
282 '/errors/error_document.html', {'c': c}, request=request,
290 '/errors/error_document.html', {'c': c}, request=request,
283 response=base_response)
291 response=base_response)
@@ -42,6 +42,7 b" STATIC_FILE_PREFIX = '/_static'"
42 URL_NAME_REQUIREMENTS = {
42 URL_NAME_REQUIREMENTS = {
43 # group name can have a slash in them, but they must not end with a slash
43 # group name can have a slash in them, but they must not end with a slash
44 'group_name': r'.*?[^/]',
44 'group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
45 # repo names can have a slash in them, but they must not end with a slash
46 # repo names can have a slash in them, but they must not end with a slash
46 'repo_name': r'.*?[^/]',
47 'repo_name': r'.*?[^/]',
47 # file path eats up everything at the end
48 # file path eats up everything at the end
@@ -531,9 +532,7 b' def make_map(config):'
531 action='my_account_update', conditions={'method': ['POST']})
532 action='my_account_update', conditions={'method': ['POST']})
532
533
533 m.connect('my_account_password', '/my_account/password',
534 m.connect('my_account_password', '/my_account/password',
534 action='my_account_password', conditions={'method': ['GET']})
535 action='my_account_password', conditions={'method': ['GET', 'POST']})
535 m.connect('my_account_password', '/my_account/password',
536 action='my_account_password_update', conditions={'method': ['POST']})
537
536
538 m.connect('my_account_repos', '/my_account/repos',
537 m.connect('my_account_repos', '/my_account/repos',
539 action='my_account_repos', conditions={'method': ['GET']})
538 action='my_account_repos', conditions={'method': ['GET']})
@@ -32,17 +32,21 b' from pylons.controllers.util import redi'
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from sqlalchemy.orm import joinedload
33 from sqlalchemy.orm import joinedload
34
34
35 from rhodecode import forms
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import auth
37 from rhodecode.lib import auth
37 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
38 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
39 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import jsonify
40 from rhodecode.lib.utils2 import safe_int, md5
42 from rhodecode.lib.utils2 import safe_int, md5
41 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.ext_json import json
44
45 from rhodecode.model.validation_schema.schemas import user_schema
42 from rhodecode.model.db import (
46 from rhodecode.model.db import (
43 Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
47 Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
44 UserFollowing)
48 UserFollowing)
45 from rhodecode.model.forms import UserForm, PasswordChangeForm
49 from rhodecode.model.forms import UserForm
46 from rhodecode.model.scm import RepoList
50 from rhodecode.model.scm import RepoList
47 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
48 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.repo import RepoModel
@@ -185,38 +189,44 b' class MyAccountController(BaseController'
185 force_defaults=False
189 force_defaults=False
186 )
190 )
187
191
188 @auth.CSRFRequired()
192 @auth.CSRFRequired(except_methods=['GET'])
189 def my_account_password_update(self):
190 c.active = 'password'
191 self.__load_data()
192 _form = PasswordChangeForm(c.rhodecode_user.username)()
193 try:
194 form_result = _form.to_python(request.POST)
195 UserModel().update_user(c.rhodecode_user.user_id, **form_result)
196 instance = c.rhodecode_user.get_instance()
197 instance.update_userdata(force_password_change=False)
198 Session().commit()
199 session.setdefault('rhodecode_user', {}).update(
200 {'password': md5(instance.password)})
201 session.save()
202 h.flash(_("Successfully updated password"), category='success')
203 except formencode.Invalid as errors:
204 return htmlfill.render(
205 render('admin/my_account/my_account.html'),
206 defaults=errors.value,
207 errors=errors.error_dict or {},
208 prefix_error=False,
209 encoding="UTF-8",
210 force_defaults=False)
211 except Exception:
212 log.exception("Exception updating password")
213 h.flash(_('Error occurred during update of user password'),
214 category='error')
215 return render('admin/my_account/my_account.html')
216
217 def my_account_password(self):
193 def my_account_password(self):
218 c.active = 'password'
194 c.active = 'password'
219 self.__load_data()
195 self.__load_data()
196
197 schema = user_schema.ChangePasswordSchema().bind(
198 username=c.rhodecode_user.username)
199
200 form = forms.Form(schema,
201 buttons=(forms.buttons.save, forms.buttons.reset))
202
203 if request.method == 'POST':
204 controls = request.POST.items()
205 try:
206 valid_data = form.validate(controls)
207 UserModel().update_user(c.rhodecode_user.user_id, **valid_data)
208 instance = c.rhodecode_user.get_instance()
209 instance.update_userdata(force_password_change=False)
210 Session().commit()
211 except forms.ValidationFailure as e:
212 request.session.flash(
213 _('Error occurred during update of user password'),
214 queue='error')
215 form = e
216 except Exception:
217 log.exception("Exception updating password")
218 request.session.flash(
219 _('Error occurred during update of user password'),
220 queue='error')
221 else:
222 session.setdefault('rhodecode_user', {}).update(
223 {'password': md5(instance.password)})
224 session.save()
225 request.session.flash(
226 _("Successfully updated password"), queue='success')
227 return redirect(url('my_account_password'))
228
229 c.form = form
220 return render('admin/my_account/my_account.html')
230 return render('admin/my_account/my_account.html')
221
231
222 def my_account_repos(self):
232 def my_account_repos(self):
@@ -352,11 +362,10 b' class MyAccountController(BaseController'
352 return render('admin/my_account/my_account.html')
362 return render('admin/my_account/my_account.html')
353
363
354 @auth.CSRFRequired()
364 @auth.CSRFRequired()
365 @jsonify
355 def my_notifications_toggle_visibility(self):
366 def my_notifications_toggle_visibility(self):
356 user = c.rhodecode_user.get_instance()
367 user = c.rhodecode_user.get_instance()
357 user_data = user.user_data
368 new_status = not user.user_data.get('notification_status', True)
358 status = user_data.get('notification_status', False)
369 user.update_userdata(notification_status=new_status)
359 user_data['notification_status'] = not status
360 user.user_data = user_data
361 Session().commit()
370 Session().commit()
362 return redirect(url('my_account_notifications'))
371 return user.user_data['notification_status']
@@ -135,6 +135,7 b' class SettingsController(BaseController)'
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136
136
137 application_form = ApplicationUiSettingsForm()()
137 application_form = ApplicationUiSettingsForm()()
138
138 try:
139 try:
139 form_result = application_form.to_python(dict(request.POST))
140 form_result = application_form.to_python(dict(request.POST))
140 except formencode.Invalid as errors:
141 except formencode.Invalid as errors:
@@ -151,12 +152,14 b' class SettingsController(BaseController)'
151 )
152 )
152
153
153 try:
154 try:
154 model.update_global_ssl_setting(form_result['web_push_ssl'])
155 if c.visual.allow_repo_location_change:
155 if c.visual.allow_repo_location_change:
156 model.update_global_path_setting(
156 model.update_global_path_setting(
157 form_result['paths_root_path'])
157 form_result['paths_root_path'])
158
159 model.update_global_ssl_setting(form_result['web_push_ssl'])
158 model.update_global_hook_settings(form_result)
160 model.update_global_hook_settings(form_result)
159 model.create_global_svn_settings(form_result)
161
162 model.create_or_update_global_svn_settings(form_result)
160 model.create_or_update_global_hg_settings(form_result)
163 model.create_or_update_global_hg_settings(form_result)
161 model.create_or_update_global_pr_settings(form_result)
164 model.create_or_update_global_pr_settings(form_result)
162 except Exception:
165 except Exception:
@@ -789,18 +792,5 b' LabSetting = collections.namedtuple('
789 # This list has to be kept in sync with the form
792 # This list has to be kept in sync with the form
790 # rhodecode.model.forms.LabsSettingsForm.
793 # rhodecode.model.forms.LabsSettingsForm.
791 _LAB_SETTINGS = [
794 _LAB_SETTINGS = [
792 LabSetting(
795
793 key='rhodecode_proxy_subversion_http_requests',
794 type='bool',
795 group=lazy_ugettext('Subversion HTTP Support'),
796 label=lazy_ugettext('Proxy subversion HTTP requests'),
797 help='' # Do not translate the empty string!
798 ),
799 LabSetting(
800 key='rhodecode_subversion_http_server_url',
801 type='str',
802 group=lazy_ugettext('Subversion HTTP Server URL'),
803 label='', # Do not translate the empty string!
804 help=lazy_ugettext('e.g. http://localhost:8080/')
805 ),
806 ]
796 ]
@@ -44,6 +44,8 b' from rhodecode.lib.vcs.backends.base imp'
44 from rhodecode.lib.vcs.exceptions import (
44 from rhodecode.lib.vcs.exceptions import (
45 CommitError, EmptyRepositoryError, NodeDoesNotExistError)
45 CommitError, EmptyRepositoryError, NodeDoesNotExistError)
46 from rhodecode.model.db import Statistics, CacheKey, User
46 from rhodecode.model.db import Statistics, CacheKey, User
47 from rhodecode.model.repo import ReadmeFinder
48
47
49
48 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
49
51
@@ -61,37 +63,16 b' class SummaryController(BaseRepoControll'
61 @cache_region('long_term')
63 @cache_region('long_term')
62 def _generate_readme(cache_key):
64 def _generate_readme(cache_key):
63 readme_data = None
65 readme_data = None
64 readme_file = None
66 readme_node = None
65 try:
67 readme_filename = None
66 # gets the landing revision or tip if fails
68 commit = self._get_landing_commit_or_none(db_repo)
67 commit = db_repo.get_landing_commit()
69 if commit:
68 if isinstance(commit, EmptyCommit):
70 log.debug("Searching for a README file.")
69 raise EmptyRepositoryError()
71 readme_node = ReadmeFinder(default_renderer).search(commit)
70 renderer = MarkupRenderer()
72 if readme_node:
71 for f in renderer.pick_readme_order(default_renderer):
73 readme_data = self._render_readme_or_none(commit, readme_node)
72 try:
74 readme_filename = readme_node.path
73 node = commit.get_node(f)
75 return readme_data, readme_filename
74 except NodeDoesNotExistError:
75 continue
76
77 if not node.is_file():
78 continue
79
80 readme_file = f
81 log.debug('Found README file `%s` rendering...',
82 readme_file)
83 readme_data = renderer.render(node.content,
84 filename=f)
85 break
86 except CommitError:
87 log.exception("Problem getting commit")
88 pass
89 except EmptyRepositoryError:
90 pass
91 except Exception:
92 log.exception("General failure")
93
94 return readme_data, readme_file
95
76
96 invalidator_context = CacheKey.repo_context_cache(
77 invalidator_context = CacheKey.repo_context_cache(
97 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
78 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
@@ -102,11 +83,36 b' class SummaryController(BaseRepoControll'
102
83
103 return computed
84 return computed
104
85
86 def _get_landing_commit_or_none(self, db_repo):
87 log.debug("Getting the landing commit.")
88 try:
89 commit = db_repo.get_landing_commit()
90 if not isinstance(commit, EmptyCommit):
91 return commit
92 else:
93 log.debug("Repository is empty, no README to render.")
94 except CommitError:
95 log.exception(
96 "Problem getting commit when trying to render the README.")
97
98 def _render_readme_or_none(self, commit, readme_node):
99 log.debug(
100 'Found README file `%s` rendering...', readme_node.path)
101 renderer = MarkupRenderer()
102 try:
103 return renderer.render(
104 readme_node.content, filename=readme_node.path)
105 except Exception:
106 log.exception(
107 "Exception while trying to render the README")
105
108
106 @LoginRequired()
109 @LoginRequired()
107 @HasRepoPermissionAnyDecorator(
110 @HasRepoPermissionAnyDecorator(
108 'repository.read', 'repository.write', 'repository.admin')
111 'repository.read', 'repository.write', 'repository.admin')
109 def index(self, repo_name):
112 def index(self, repo_name):
113
114 # Prepare the clone URL
115
110 username = ''
116 username = ''
111 if c.rhodecode_user.username != User.DEFAULT_USER:
117 if c.rhodecode_user.username != User.DEFAULT_USER:
112 username = safe_str(c.rhodecode_user.username)
118 username = safe_str(c.rhodecode_user.username)
@@ -124,6 +130,8 b' class SummaryController(BaseRepoControll'
124 c.clone_repo_url_id = c.rhodecode_db_repo.clone_url(
130 c.clone_repo_url_id = c.rhodecode_db_repo.clone_url(
125 user=username, uri_tmpl=_def_clone_uri_by_id)
131 user=username, uri_tmpl=_def_clone_uri_by_id)
126
132
133 # If enabled, get statistics data
134
127 c.show_stats = bool(c.rhodecode_db_repo.enable_statistics)
135 c.show_stats = bool(c.rhodecode_db_repo.enable_statistics)
128
136
129 stats = self.sa.query(Statistics)\
137 stats = self.sa.query(Statistics)\
@@ -47,7 +47,7 b' def _commits_as_dict(commit_ids, repos):'
47 if not commit_ids:
47 if not commit_ids:
48 return []
48 return []
49
49
50 needed_commits = set(commit_ids)
50 needed_commits = list(commit_ids)
51
51
52 commits = []
52 commits = []
53 reviewers = []
53 reviewers = []
@@ -57,6 +57,7 b' def _commits_as_dict(commit_ids, repos):'
57
57
58 vcs_repo = repo.scm_instance(cache=False)
58 vcs_repo = repo.scm_instance(cache=False)
59 try:
59 try:
60 # use copy of needed_commits since we modify it while iterating
60 for commit_id in list(needed_commits):
61 for commit_id in list(needed_commits):
61 try:
62 try:
62 cs = vcs_repo.get_changeset(commit_id)
63 cs = vcs_repo.get_changeset(commit_id)
@@ -78,7 +79,7 b' def _commits_as_dict(commit_ids, repos):'
78 repo.repo_name)
79 repo.repo_name)
79 commits.append(cs_data)
80 commits.append(cs_data)
80
81
81 needed_commits.discard(commit_id)
82 needed_commits.remove(commit_id)
82
83
83 except Exception as e:
84 except Exception as e:
84 log.exception(e)
85 log.exception(e)
@@ -1,21 +1,22 b''
1 # English translations for rhodecode.
1 # Translations template for rhodecode-enterprise-ce.
2 # Copyright (C) 2015 RhodeCode GmbH
2 # Copyright (C) 2016 RhodeCode GmbH
3 # This file is distributed under the same license as the rhodecode project.
3 # This file is distributed under the same license as the rhodecode-enterprise-ce project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
5 #
4 #
5 # Translators:
6 msgid ""
6 msgid ""
7 msgstr ""
7 msgstr ""
8 "Project-Id-Version: rhodecode 0.1\n"
8 "Project-Id-Version: RhodeCode\n"
9 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
9 "Report-Msgid-Bugs-To: marcin@rhodecode.com\n"
10 "POT-Creation-Date: 2013-06-01 18:38+0200\n"
10 "POT-Creation-Date: 2013-06-01 18:38+0200\n"
11 "PO-Revision-Date: 2011-02-25 19:13+0100\n"
11 "PO-Revision-Date: 2011-02-25 19:13+0100\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Last-Translator: Marcin Kuzminski <marcin@rhodecode.com>\n"
13 "Language-Team: en <LL@li.org>\n"
13 "Language-Team: en <admin@rhodecode.com>\n"
14 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
15 "MIME-Version: 1.0\n"
14 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=utf-8\n"
15 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
16 "Content-Transfer-Encoding: 8bit\n"
18 "Generated-By: Babel 0.9.6\n"
17 "Generated-By: Babel 1.3\n"
18 "Language: en\n"
19 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
19
20
20 #: rhodecode/controllers/changelog.py:149
21 #: rhodecode/controllers/changelog.py:149
21 msgid "All Branches"
22 msgid "All Branches"
@@ -20,7 +20,7 b''
20
20
21 import logging
21 import logging
22
22
23 from rhodecode.model.db import Repository, Integration
23 from rhodecode.model.db import Repository, Integration, RepoGroup
24 from rhodecode.config.routing import (
24 from rhodecode.config.routing import (
25 ADMIN_PREFIX, add_route_requirements, URL_NAME_REQUIREMENTS)
25 ADMIN_PREFIX, add_route_requirements, URL_NAME_REQUIREMENTS)
26 from rhodecode.integrations import integration_type_registry
26 from rhodecode.integrations import integration_type_registry
@@ -29,6 +29,17 b' log = logging.getLogger(__name__)'
29
29
30
30
31 def includeme(config):
31 def includeme(config):
32
33 # global integrations
34
35 config.add_route('global_integrations_new',
36 ADMIN_PREFIX + '/integrations/new')
37 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
38 attr='new_integration',
39 renderer='rhodecode:templates/admin/integrations/new.html',
40 request_method='GET',
41 route_name='global_integrations_new')
42
32 config.add_route('global_integrations_home',
43 config.add_route('global_integrations_home',
33 ADMIN_PREFIX + '/integrations')
44 ADMIN_PREFIX + '/integrations')
34 config.add_route('global_integrations_list',
45 config.add_route('global_integrations_list',
@@ -46,18 +57,80 b' def includeme(config):'
46 config.add_route('global_integrations_edit',
57 config.add_route('global_integrations_edit',
47 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
58 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
48 custom_predicates=(valid_integration,))
59 custom_predicates=(valid_integration,))
60
61
49 for route_name in ['global_integrations_create', 'global_integrations_edit']:
62 for route_name in ['global_integrations_create', 'global_integrations_edit']:
50 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
63 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
51 attr='settings_get',
64 attr='settings_get',
52 renderer='rhodecode:templates/admin/integrations/edit.html',
65 renderer='rhodecode:templates/admin/integrations/form.html',
53 request_method='GET',
66 request_method='GET',
54 route_name=route_name)
67 route_name=route_name)
55 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
68 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
56 attr='settings_post',
69 attr='settings_post',
57 renderer='rhodecode:templates/admin/integrations/edit.html',
70 renderer='rhodecode:templates/admin/integrations/form.html',
58 request_method='POST',
71 request_method='POST',
59 route_name=route_name)
72 route_name=route_name)
60
73
74
75 # repo group integrations
76 config.add_route('repo_group_integrations_home',
77 add_route_requirements(
78 '{repo_group_name}/settings/integrations',
79 URL_NAME_REQUIREMENTS
80 ),
81 custom_predicates=(valid_repo_group,)
82 )
83 config.add_route('repo_group_integrations_list',
84 add_route_requirements(
85 '{repo_group_name}/settings/integrations/{integration}',
86 URL_NAME_REQUIREMENTS
87 ),
88 custom_predicates=(valid_repo_group, valid_integration))
89 for route_name in ['repo_group_integrations_home', 'repo_group_integrations_list']:
90 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
91 attr='index',
92 renderer='rhodecode:templates/admin/integrations/list.html',
93 request_method='GET',
94 route_name=route_name)
95
96 config.add_route('repo_group_integrations_new',
97 add_route_requirements(
98 '{repo_group_name}/settings/integrations/new',
99 URL_NAME_REQUIREMENTS
100 ),
101 custom_predicates=(valid_repo_group,))
102 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
103 attr='new_integration',
104 renderer='rhodecode:templates/admin/integrations/new.html',
105 request_method='GET',
106 route_name='repo_group_integrations_new')
107
108 config.add_route('repo_group_integrations_create',
109 add_route_requirements(
110 '{repo_group_name}/settings/integrations/{integration}/new',
111 URL_NAME_REQUIREMENTS
112 ),
113 custom_predicates=(valid_repo_group, valid_integration))
114 config.add_route('repo_group_integrations_edit',
115 add_route_requirements(
116 '{repo_group_name}/settings/integrations/{integration}/{integration_id}',
117 URL_NAME_REQUIREMENTS
118 ),
119 custom_predicates=(valid_repo_group, valid_integration))
120 for route_name in ['repo_group_integrations_edit', 'repo_group_integrations_create']:
121 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
122 attr='settings_get',
123 renderer='rhodecode:templates/admin/integrations/form.html',
124 request_method='GET',
125 route_name=route_name)
126 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
127 attr='settings_post',
128 renderer='rhodecode:templates/admin/integrations/form.html',
129 request_method='POST',
130 route_name=route_name)
131
132
133 # repo integrations
61 config.add_route('repo_integrations_home',
134 config.add_route('repo_integrations_home',
62 add_route_requirements(
135 add_route_requirements(
63 '{repo_name}/settings/integrations',
136 '{repo_name}/settings/integrations',
@@ -74,8 +147,21 b' def includeme(config):'
74 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
147 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
75 attr='index',
148 attr='index',
76 request_method='GET',
149 request_method='GET',
150 renderer='rhodecode:templates/admin/integrations/list.html',
77 route_name=route_name)
151 route_name=route_name)
78
152
153 config.add_route('repo_integrations_new',
154 add_route_requirements(
155 '{repo_name}/settings/integrations/new',
156 URL_NAME_REQUIREMENTS
157 ),
158 custom_predicates=(valid_repo,))
159 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
160 attr='new_integration',
161 renderer='rhodecode:templates/admin/integrations/new.html',
162 request_method='GET',
163 route_name='repo_integrations_new')
164
79 config.add_route('repo_integrations_create',
165 config.add_route('repo_integrations_create',
80 add_route_requirements(
166 add_route_requirements(
81 '{repo_name}/settings/integrations/{integration}/new',
167 '{repo_name}/settings/integrations/{integration}/new',
@@ -91,12 +177,12 b' def includeme(config):'
91 for route_name in ['repo_integrations_edit', 'repo_integrations_create']:
177 for route_name in ['repo_integrations_edit', 'repo_integrations_create']:
92 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
178 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
93 attr='settings_get',
179 attr='settings_get',
94 renderer='rhodecode:templates/admin/integrations/edit.html',
180 renderer='rhodecode:templates/admin/integrations/form.html',
95 request_method='GET',
181 request_method='GET',
96 route_name=route_name)
182 route_name=route_name)
97 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
183 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
98 attr='settings_post',
184 attr='settings_post',
99 renderer='rhodecode:templates/admin/integrations/edit.html',
185 renderer='rhodecode:templates/admin/integrations/form.html',
100 request_method='POST',
186 request_method='POST',
101 route_name=route_name)
187 route_name=route_name)
102
188
@@ -107,20 +193,37 b' def valid_repo(info, request):'
107 return True
193 return True
108
194
109
195
196 def valid_repo_group(info, request):
197 repo_group = RepoGroup.get_by_group_name(info['match']['repo_group_name'])
198 if repo_group:
199 return True
200 return False
201
202
110 def valid_integration(info, request):
203 def valid_integration(info, request):
111 integration_type = info['match']['integration']
204 integration_type = info['match']['integration']
112 integration_id = info['match'].get('integration_id')
205 integration_id = info['match'].get('integration_id')
113 repo_name = info['match'].get('repo_name')
206 repo_name = info['match'].get('repo_name')
207 repo_group_name = info['match'].get('repo_group_name')
114
208
115 if integration_type not in integration_type_registry:
209 if integration_type not in integration_type_registry:
116 return False
210 return False
117
211
118 repo = None
212 repo, repo_group = None, None
119 if repo_name:
213 if repo_name:
120 repo = Repository.get_by_repo_name(info['match']['repo_name'])
214 repo = Repository.get_by_repo_name(repo_name)
121 if not repo:
215 if not repo:
122 return False
216 return False
123
217
218 if repo_group_name:
219 repo_group = RepoGroup.get_by_group_name(repo_group_name)
220 if not repo_group:
221 return False
222
223 if repo_name and repo_group:
224 raise Exception('Either repo or repo_group can be set, not both')
225
226
124 if integration_id:
227 if integration_id:
125 integration = Integration.get(integration_id)
228 integration = Integration.get(integration_id)
126 if not integration:
229 if not integration:
@@ -129,5 +232,7 b' def valid_integration(info, request):'
129 return False
232 return False
130 if repo and repo.repo_id != integration.repo_id:
233 if repo and repo.repo_id != integration.repo_id:
131 return False
234 return False
235 if repo_group and repo_group.group_id != integration.repo_group_id:
236 return False
132
237
133 return True
238 return True
@@ -20,26 +20,52 b''
20
20
21 import colander
21 import colander
22
22
23 from rhodecode.translation import lazy_ugettext
23 from rhodecode.translation import _
24
24
25
25
26 class IntegrationSettingsSchemaBase(colander.MappingSchema):
26 class IntegrationOptionsSchemaBase(colander.MappingSchema):
27 """
28 This base schema is intended for use in integrations.
29 It adds a few default settings (e.g., "enabled"), so that integration
30 authors don't have to maintain a bunch of boilerplate.
31 """
32 enabled = colander.SchemaNode(
27 enabled = colander.SchemaNode(
33 colander.Bool(),
28 colander.Bool(),
34 default=True,
29 default=True,
35 description=lazy_ugettext('Enable or disable this integration.'),
30 description=_('Enable or disable this integration.'),
36 missing=False,
31 missing=False,
37 title=lazy_ugettext('Enabled'),
32 title=_('Enabled'),
38 )
33 )
39
34
40 name = colander.SchemaNode(
35 name = colander.SchemaNode(
41 colander.String(),
36 colander.String(),
42 description=lazy_ugettext('Short name for this integration.'),
37 description=_('Short name for this integration.'),
43 missing=colander.required,
38 missing=colander.required,
44 title=lazy_ugettext('Integration name'),
39 title=_('Integration name'),
45 )
40 )
41
42
43 class RepoIntegrationOptionsSchema(IntegrationOptionsSchemaBase):
44 pass
45
46
47 class RepoGroupIntegrationOptionsSchema(IntegrationOptionsSchemaBase):
48 child_repos_only = colander.SchemaNode(
49 colander.Bool(),
50 default=True,
51 description=_(
52 'Limit integrations to to work only on the direct children '
53 'repositories of this repository group (no subgroups)'),
54 missing=False,
55 title=_('Limit to childen repos only'),
56 )
57
58
59 class GlobalIntegrationOptionsSchema(IntegrationOptionsSchemaBase):
60 child_repos_only = colander.SchemaNode(
61 colander.Bool(),
62 default=False,
63 description=_(
64 'Limit integrations to to work only on root level repositories'),
65 missing=False,
66 title=_('Root repositories only'),
67 )
68
69
70 class IntegrationSettingsSchemaBase(colander.MappingSchema):
71 pass
@@ -18,25 +18,84 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
21 import colander
22 from rhodecode.translation import _
22
23
23
24
24 class IntegrationTypeBase(object):
25 class IntegrationTypeBase(object):
25 """ Base class for IntegrationType plugins """
26 """ Base class for IntegrationType plugins """
26
27
28 description = ''
29 icon = '''
30 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
31 <svg
32 xmlns:dc="http://purl.org/dc/elements/1.1/"
33 xmlns:cc="http://creativecommons.org/ns#"
34 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
35 xmlns:svg="http://www.w3.org/2000/svg"
36 xmlns="http://www.w3.org/2000/svg"
37 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
38 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
39 viewBox="0 -256 1792 1792"
40 id="svg3025"
41 version="1.1"
42 inkscape:version="0.48.3.1 r9886"
43 width="100%"
44 height="100%"
45 sodipodi:docname="cog_font_awesome.svg">
46 <metadata
47 id="metadata3035">
48 <rdf:RDF>
49 <cc:Work
50 rdf:about="">
51 <dc:format>image/svg+xml</dc:format>
52 <dc:type
53 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
54 </cc:Work>
55 </rdf:RDF>
56 </metadata>
57 <defs
58 id="defs3033" />
59 <sodipodi:namedview
60 pagecolor="#ffffff"
61 bordercolor="#666666"
62 borderopacity="1"
63 objecttolerance="10"
64 gridtolerance="10"
65 guidetolerance="10"
66 inkscape:pageopacity="0"
67 inkscape:pageshadow="2"
68 inkscape:window-width="640"
69 inkscape:window-height="480"
70 id="namedview3031"
71 showgrid="false"
72 inkscape:zoom="0.13169643"
73 inkscape:cx="896"
74 inkscape:cy="896"
75 inkscape:window-x="0"
76 inkscape:window-y="25"
77 inkscape:window-maximized="0"
78 inkscape:current-layer="svg3025" />
79 <g
80 transform="matrix(1,0,0,-1,121.49153,1285.4237)"
81 id="g3027">
82 <path
83 d="m 1024,640 q 0,106 -75,181 -75,75 -181,75 -106,0 -181,-75 -75,-75 -75,-181 0,-106 75,-181 75,-75 181,-75 106,0 181,75 75,75 75,181 z m 512,109 V 527 q 0,-12 -8,-23 -8,-11 -20,-13 l -185,-28 q -19,-54 -39,-91 35,-50 107,-138 10,-12 10,-25 0,-13 -9,-23 -27,-37 -99,-108 -72,-71 -94,-71 -12,0 -26,9 l -138,108 q -44,-23 -91,-38 -16,-136 -29,-186 -7,-28 -36,-28 H 657 q -14,0 -24.5,8.5 Q 622,-111 621,-98 L 593,86 q -49,16 -90,37 L 362,16 Q 352,7 337,7 323,7 312,18 186,132 147,186 q -7,10 -7,23 0,12 8,23 15,21 51,66.5 36,45.5 54,70.5 -27,50 -41,99 L 29,495 Q 16,497 8,507.5 0,518 0,531 v 222 q 0,12 8,23 8,11 19,13 l 186,28 q 14,46 39,92 -40,57 -107,138 -10,12 -10,24 0,10 9,23 26,36 98.5,107.5 72.5,71.5 94.5,71.5 13,0 26,-10 l 138,-107 q 44,23 91,38 16,136 29,186 7,28 36,28 h 222 q 14,0 24.5,-8.5 Q 914,1391 915,1378 l 28,-184 q 49,-16 90,-37 l 142,107 q 9,9 24,9 13,0 25,-10 129,-119 165,-170 7,-8 7,-22 0,-12 -8,-23 -15,-21 -51,-66.5 -36,-45.5 -54,-70.5 26,-50 41,-98 l 183,-28 q 13,-2 21,-12.5 8,-10.5 8,-23.5 z"
84 id="path3029"
85 inkscape:connector-curvature="0"
86 style="fill:currentColor" />
87 </g>
88 </svg>
89 '''
90
27 def __init__(self, settings):
91 def __init__(self, settings):
28 """
92 """
29 :param settings: dict of settings to be used for the integration
93 :param settings: dict of settings to be used for the integration
30 """
94 """
31 self.settings = settings
95 self.settings = settings
32
96
33
34 def settings_schema(self):
97 def settings_schema(self):
35 """
98 """
36 A colander schema of settings for the integration type
99 A colander schema of settings for the integration type
37
38 Subclasses can return their own schema but should always
39 inherit from IntegrationSettingsSchemaBase
40 """
100 """
41 return IntegrationSettingsSchemaBase()
101 return colander.Schema()
42
@@ -26,11 +26,10 b' import colander'
26 from mako.template import Template
26 from mako.template import Template
27
27
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.translation import _, lazy_ugettext
29 from rhodecode.translation import _
30 from rhodecode.lib.celerylib import run_task
30 from rhodecode.lib.celerylib import run_task
31 from rhodecode.lib.celerylib import tasks
31 from rhodecode.lib.celerylib import tasks
32 from rhodecode.integrations.types.base import IntegrationTypeBase
32 from rhodecode.integrations.types.base import IntegrationTypeBase
33 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
34
33
35
34
36 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
@@ -147,18 +146,79 b" repo_push_template_html = Template('''"
147 </html>
146 </html>
148 ''')
147 ''')
149
148
149 email_icon = '''
150 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
151 <svg
152 xmlns:dc="http://purl.org/dc/elements/1.1/"
153 xmlns:cc="http://creativecommons.org/ns#"
154 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
155 xmlns:svg="http://www.w3.org/2000/svg"
156 xmlns="http://www.w3.org/2000/svg"
157 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
158 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
159 viewBox="0 -256 1850 1850"
160 id="svg2989"
161 version="1.1"
162 inkscape:version="0.48.3.1 r9886"
163 width="100%"
164 height="100%"
165 sodipodi:docname="envelope_font_awesome.svg">
166 <metadata
167 id="metadata2999">
168 <rdf:RDF>
169 <cc:Work
170 rdf:about="">
171 <dc:format>image/svg+xml</dc:format>
172 <dc:type
173 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
174 </cc:Work>
175 </rdf:RDF>
176 </metadata>
177 <defs
178 id="defs2997" />
179 <sodipodi:namedview
180 pagecolor="#ffffff"
181 bordercolor="#666666"
182 borderopacity="1"
183 objecttolerance="10"
184 gridtolerance="10"
185 guidetolerance="10"
186 inkscape:pageopacity="0"
187 inkscape:pageshadow="2"
188 inkscape:window-width="640"
189 inkscape:window-height="480"
190 id="namedview2995"
191 showgrid="false"
192 inkscape:zoom="0.13169643"
193 inkscape:cx="896"
194 inkscape:cy="896"
195 inkscape:window-x="0"
196 inkscape:window-y="25"
197 inkscape:window-maximized="0"
198 inkscape:current-layer="svg2989" />
199 <g
200 transform="matrix(1,0,0,-1,37.966102,1282.678)"
201 id="g2991">
202 <path
203 d="m 1664,32 v 768 q -32,-36 -69,-66 -268,-206 -426,-338 -51,-43 -83,-67 -32,-24 -86.5,-48.5 Q 945,256 897,256 h -1 -1 Q 847,256 792.5,280.5 738,305 706,329 674,353 623,396 465,528 197,734 160,764 128,800 V 32 Q 128,19 137.5,9.5 147,0 160,0 h 1472 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 0,1051 v 11 13.5 q 0,0 -0.5,13 -0.5,13 -3,12.5 -2.5,-0.5 -5.5,9 -3,9.5 -9,7.5 -6,-2 -14,2.5 H 160 q -13,0 -22.5,-9.5 Q 128,1133 128,1120 128,952 275,836 468,684 676,519 682,514 711,489.5 740,465 757,452 774,439 801.5,420.5 829,402 852,393 q 23,-9 43,-9 h 1 1 q 20,0 43,9 23,9 50.5,27.5 27.5,18.5 44.5,31.5 17,13 46,37.5 29,24.5 35,29.5 208,165 401,317 54,43 100.5,115.5 46.5,72.5 46.5,131.5 z m 128,37 V 32 q 0,-66 -47,-113 -47,-47 -113,-47 H 160 Q 94,-128 47,-81 0,-34 0,32 v 1088 q 0,66 47,113 47,47 113,47 h 1472 q 66,0 113,-47 47,-47 47,-113 z"
204 id="path2993"
205 inkscape:connector-curvature="0"
206 style="fill:currentColor" />
207 </g>
208 </svg>
209 '''
150
210
151 class EmailSettingsSchema(IntegrationSettingsSchemaBase):
211 class EmailSettingsSchema(colander.Schema):
152 @colander.instantiate(validator=colander.Length(min=1))
212 @colander.instantiate(validator=colander.Length(min=1))
153 class recipients(colander.SequenceSchema):
213 class recipients(colander.SequenceSchema):
154 title = lazy_ugettext('Recipients')
214 title = _('Recipients')
155 description = lazy_ugettext('Email addresses to send push events to')
215 description = _('Email addresses to send push events to')
156 widget = deform.widget.SequenceWidget(min_len=1)
216 widget = deform.widget.SequenceWidget(min_len=1)
157
217
158 recipient = colander.SchemaNode(
218 recipient = colander.SchemaNode(
159 colander.String(),
219 colander.String(),
160 title=lazy_ugettext('Email address'),
220 title=_('Email address'),
161 description=lazy_ugettext('Email address'),
221 description=_('Email address'),
162 default='',
222 default='',
163 validator=colander.Email(),
223 validator=colander.Email(),
164 widget=deform.widget.TextInputWidget(
224 widget=deform.widget.TextInputWidget(
@@ -169,8 +229,9 b' class EmailSettingsSchema(IntegrationSet'
169
229
170 class EmailIntegrationType(IntegrationTypeBase):
230 class EmailIntegrationType(IntegrationTypeBase):
171 key = 'email'
231 key = 'email'
172 display_name = lazy_ugettext('Email')
232 display_name = _('Email')
173 SettingsSchema = EmailSettingsSchema
233 description = _('Send repo push summaries to a list of recipients via email')
234 icon = email_icon
174
235
175 def settings_schema(self):
236 def settings_schema(self):
176 schema = EmailSettingsSchema()
237 schema = EmailSettingsSchema()
@@ -29,29 +29,28 b' from celery.task import task'
29 from mako.template import Template
29 from mako.template import Template
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.translation import lazy_ugettext
32 from rhodecode.translation import _
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.celerylib import run_task
34 from rhodecode.lib.celerylib import run_task
35 from rhodecode.lib.colander_utils import strip_whitespace
35 from rhodecode.lib.colander_utils import strip_whitespace
36 from rhodecode.integrations.types.base import IntegrationTypeBase
36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38
37
39 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
40
39
41
40
42 class HipchatSettingsSchema(IntegrationSettingsSchemaBase):
41 class HipchatSettingsSchema(colander.Schema):
43 color_choices = [
42 color_choices = [
44 ('yellow', lazy_ugettext('Yellow')),
43 ('yellow', _('Yellow')),
45 ('red', lazy_ugettext('Red')),
44 ('red', _('Red')),
46 ('green', lazy_ugettext('Green')),
45 ('green', _('Green')),
47 ('purple', lazy_ugettext('Purple')),
46 ('purple', _('Purple')),
48 ('gray', lazy_ugettext('Gray')),
47 ('gray', _('Gray')),
49 ]
48 ]
50
49
51 server_url = colander.SchemaNode(
50 server_url = colander.SchemaNode(
52 colander.String(),
51 colander.String(),
53 title=lazy_ugettext('Hipchat server URL'),
52 title=_('Hipchat server URL'),
54 description=lazy_ugettext('Hipchat integration url.'),
53 description=_('Hipchat integration url.'),
55 default='',
54 default='',
56 preparer=strip_whitespace,
55 preparer=strip_whitespace,
57 validator=colander.url,
56 validator=colander.url,
@@ -61,15 +60,15 b' class HipchatSettingsSchema(IntegrationS'
61 )
60 )
62 notify = colander.SchemaNode(
61 notify = colander.SchemaNode(
63 colander.Bool(),
62 colander.Bool(),
64 title=lazy_ugettext('Notify'),
63 title=_('Notify'),
65 description=lazy_ugettext('Make a notification to the users in room.'),
64 description=_('Make a notification to the users in room.'),
66 missing=False,
65 missing=False,
67 default=False,
66 default=False,
68 )
67 )
69 color = colander.SchemaNode(
68 color = colander.SchemaNode(
70 colander.String(),
69 colander.String(),
71 title=lazy_ugettext('Color'),
70 title=_('Color'),
72 description=lazy_ugettext('Background color of message.'),
71 description=_('Background color of message.'),
73 missing='',
72 missing='',
74 validator=colander.OneOf([x[0] for x in color_choices]),
73 validator=colander.OneOf([x[0] for x in color_choices]),
75 widget=deform.widget.Select2Widget(
74 widget=deform.widget.Select2Widget(
@@ -79,29 +78,28 b' class HipchatSettingsSchema(IntegrationS'
79
78
80
79
81 repo_push_template = Template('''
80 repo_push_template = Template('''
82 <b>${data['actor']['username']}</b> pushed to
81 <b>${data['actor']['username']}</b> pushed to repo <a href="${data['repo']['url']}">${data['repo']['repo_name']}</a>:
83 %if data['push']['branches']:
84 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'}
85 ${', '.join('<a href="%s">%s</a>' % (branch['url'], branch['name']) for branch in data['push']['branches'])}
86 %else:
87 unknown branch
88 %endif
89 in <a href="${data['repo']['url']}">${data['repo']['repo_name']}</a>
90 <br>
82 <br>
91 <ul>
83 <ul>
92 %for commit in data['push']['commits']:
84 %for branch, branch_commits in branches_commits.items():
93 <li>
85 <li>
94 <a href="${commit['url']}">${commit['short_id']}</a> - ${commit['message_html']}
86 <a href="${branch_commits['branch']['url']}">branch: ${branch_commits['branch']['name']}</a>
87 <ul>
88 %for commit in branch_commits['commits']:
89 <li><a href="${commit['url']}">${commit['short_id']}</a> - ${commit['message_html']}</li>
90 %endfor
91 </ul>
95 </li>
92 </li>
96 %endfor
93 %endfor
97 </ul>
98 ''')
94 ''')
99
95
100
96
101
102 class HipchatIntegrationType(IntegrationTypeBase):
97 class HipchatIntegrationType(IntegrationTypeBase):
103 key = 'hipchat'
98 key = 'hipchat'
104 display_name = lazy_ugettext('Hipchat')
99 display_name = _('Hipchat')
100 description = _('Send events such as repo pushes and pull requests to '
101 'your hipchat channel.')
102 icon = '''<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve"><g><g transform="translate(0.000000,511.000000) scale(0.100000,-0.100000)"><path fill="#205281" d="M4197.1,4662.4c-1661.5-260.4-3018-1171.6-3682.6-2473.3C219.9,1613.6,100,1120.3,100,462.6c0-1014,376.8-1918.4,1127-2699.4C2326.7-3377.6,3878.5-3898.3,5701-3730.5l486.5,44.5l208.9-123.3c637.2-373.4,1551.8-640.6,2240.4-650.9c304.9-6.9,335.7,0,417.9,75.4c185,174.7,147.3,411.1-89.1,548.1c-315.2,181.6-620,544.7-733.1,870.1l-51.4,157.6l472.7,472.7c349.4,349.4,520.7,551.5,657.7,774.2c784.5,1281.2,784.5,2788.5,0,4052.6c-236.4,376.8-794.8,966-1178.4,1236.7c-572.1,407.7-1264.1,709.1-1993.7,870.1c-267.2,58.2-479.6,75.4-1038,82.2C4714.4,4686.4,4310.2,4679.6,4197.1,4662.4z M5947.6,3740.9c1856.7-380.3,3127.6-1709.4,3127.6-3275c0-1000.3-534.4-1949.2-1466.2-2600.1c-188.4-133.6-287.8-226.1-301.5-284.4c-41.1-157.6,263.8-938.6,397.4-1020.8c20.5-10.3,34.3-44.5,34.3-75.4c0-167.8-811.9,195.3-1363.4,609.8l-181.6,137l-332.3-58.2c-445.3-78.8-1281.2-78.8-1702.6,0C2796-2569.2,1734.1-1832.6,1220.2-801.5C983.8-318.5,905,51.5,929,613.3c27.4,640.6,243.2,1192.1,685.1,1740.3c620,770.8,1661.5,1305.2,2822.8,1452.5C4806.9,3854,5553.7,3819.7,5947.6,3740.9z"/><path fill="#205281" d="M2381.5-345.9c-75.4-106.2-68.5-167.8,34.3-322c332.3-500.2,1010.6-928.4,1760.8-1120.2c417.9-106.2,1226.4-106.2,1644.3,0c712.5,181.6,1270.9,517.3,1685.4,1014C7681-561.7,7715.3-424.7,7616-325.4c-89.1,89.1-167.9,65.1-431.7-133.6c-835.8-630.3-2028-856.4-3086.5-585.8C3683.3-938.6,3142-685,2830.3-448.7C2576.8-253.4,2463.7-229.4,2381.5-345.9z"/></g></g><!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon --></svg>'''
105 valid_events = [
103 valid_events = [
106 events.PullRequestCloseEvent,
104 events.PullRequestCloseEvent,
107 events.PullRequestMergeEvent,
105 events.PullRequestMergeEvent,
@@ -217,8 +215,23 b' class HipchatIntegrationType(Integration'
217 )
215 )
218
216
219 def format_repo_push_event(self, data):
217 def format_repo_push_event(self, data):
218 branch_data = {branch['name']: branch
219 for branch in data['push']['branches']}
220
221 branches_commits = {}
222 for commit in data['push']['commits']:
223 log.critical(commit)
224 if commit['branch'] not in branches_commits:
225 branch_commits = {'branch': branch_data[commit['branch']],
226 'commits': []}
227 branches_commits[commit['branch']] = branch_commits
228
229 branch_commits = branches_commits[commit['branch']]
230 branch_commits['commits'].append(commit)
231
220 result = repo_push_template.render(
232 result = repo_push_template.render(
221 data=data,
233 data=data,
234 branches_commits=branches_commits,
222 )
235 )
223 return result
236 return result
224
237
@@ -29,21 +29,20 b' from celery.task import task'
29 from mako.template import Template
29 from mako.template import Template
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.translation import lazy_ugettext
32 from rhodecode.translation import _
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.celerylib import run_task
34 from rhodecode.lib.celerylib import run_task
35 from rhodecode.lib.colander_utils import strip_whitespace
35 from rhodecode.lib.colander_utils import strip_whitespace
36 from rhodecode.integrations.types.base import IntegrationTypeBase
36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38
37
39 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
40
39
41
40
42 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
41 class SlackSettingsSchema(colander.Schema):
43 service = colander.SchemaNode(
42 service = colander.SchemaNode(
44 colander.String(),
43 colander.String(),
45 title=lazy_ugettext('Slack service URL'),
44 title=_('Slack service URL'),
46 description=h.literal(lazy_ugettext(
45 description=h.literal(_(
47 'This can be setup at the '
46 'This can be setup at the '
48 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
47 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
49 'slack app manager</a>')),
48 'slack app manager</a>')),
@@ -56,8 +55,8 b' class SlackSettingsSchema(IntegrationSet'
56 )
55 )
57 username = colander.SchemaNode(
56 username = colander.SchemaNode(
58 colander.String(),
57 colander.String(),
59 title=lazy_ugettext('Username'),
58 title=_('Username'),
60 description=lazy_ugettext('Username to show notifications coming from.'),
59 description=_('Username to show notifications coming from.'),
61 missing='Rhodecode',
60 missing='Rhodecode',
62 preparer=strip_whitespace,
61 preparer=strip_whitespace,
63 widget=deform.widget.TextInputWidget(
62 widget=deform.widget.TextInputWidget(
@@ -66,8 +65,8 b' class SlackSettingsSchema(IntegrationSet'
66 )
65 )
67 channel = colander.SchemaNode(
66 channel = colander.SchemaNode(
68 colander.String(),
67 colander.String(),
69 title=lazy_ugettext('Channel'),
68 title=_('Channel'),
70 description=lazy_ugettext('Channel to send notifications to.'),
69 description=_('Channel to send notifications to.'),
71 missing='',
70 missing='',
72 preparer=strip_whitespace,
71 preparer=strip_whitespace,
73 widget=deform.widget.TextInputWidget(
72 widget=deform.widget.TextInputWidget(
@@ -76,8 +75,8 b' class SlackSettingsSchema(IntegrationSet'
76 )
75 )
77 icon_emoji = colander.SchemaNode(
76 icon_emoji = colander.SchemaNode(
78 colander.String(),
77 colander.String(),
79 title=lazy_ugettext('Emoji'),
78 title=_('Emoji'),
80 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
79 description=_('Emoji to use eg. :studio_microphone:'),
81 missing='',
80 missing='',
82 preparer=strip_whitespace,
81 preparer=strip_whitespace,
83 widget=deform.widget.TextInputWidget(
82 widget=deform.widget.TextInputWidget(
@@ -87,25 +86,22 b' class SlackSettingsSchema(IntegrationSet'
87
86
88
87
89 repo_push_template = Template(r'''
88 repo_push_template = Template(r'''
90 *${data['actor']['username']}* pushed to \
89 *${data['actor']['username']}* pushed to repo <${data['repo']['url']}|${data['repo']['repo_name']}>:
91 %if data['push']['branches']:
90 %for branch, branch_commits in branches_commits.items():
92 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
91 branch: <${branch_commits['branch']['url']}|${branch_commits['branch']['name']}>
93 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
92 %for commit in branch_commits['commits']:
94 %else:
93 > <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
95 unknown branch \
94 %endfor
96 %endif
97 in <${data['repo']['url']}|${data['repo']['repo_name']}>
98 >>>
99 %for commit in data['push']['commits']:
100 <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
101 %endfor
95 %endfor
102 ''')
96 ''')
103
97
104
98
105 class SlackIntegrationType(IntegrationTypeBase):
99 class SlackIntegrationType(IntegrationTypeBase):
106 key = 'slack'
100 key = 'slack'
107 display_name = lazy_ugettext('Slack')
101 display_name = _('Slack')
108 SettingsSchema = SlackSettingsSchema
102 description = _('Send events such as repo pushes and pull requests to '
103 'your slack channel.')
104 icon = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid"><g><path d="M165.963541,15.8384262 C162.07318,3.86308197 149.212328,-2.69009836 137.239082,1.20236066 C125.263738,5.09272131 118.710557,17.9535738 122.603016,29.9268197 L181.550164,211.292328 C185.597902,222.478689 197.682361,228.765377 209.282098,225.426885 C221.381246,221.943607 228.756984,209.093246 224.896,197.21023 C224.749115,196.756984 165.963541,15.8384262 165.963541,15.8384262" fill="#DFA22F"></path><path d="M74.6260984,45.515541 C70.7336393,33.5422951 57.8727869,26.9891148 45.899541,30.8794754 C33.9241967,34.7698361 27.3710164,47.6306885 31.2634754,59.6060328 L90.210623,240.971541 C94.2583607,252.157902 106.34282,258.44459 117.942557,255.104 C130.041705,251.62282 137.417443,238.772459 133.556459,226.887344 C133.409574,226.436197 74.6260984,45.515541 74.6260984,45.515541" fill="#3CB187"></path><path d="M240.161574,166.045377 C252.136918,162.155016 258.688,149.294164 254.797639,137.31882 C250.907279,125.345574 238.046426,118.792393 226.07318,122.682754 L44.7076721,181.632 C33.5213115,185.677639 27.234623,197.762098 30.5731148,209.361836 C34.0563934,221.460984 46.9067541,228.836721 58.7897705,224.975738 C59.2430164,224.828852 240.161574,166.045377 240.161574,166.045377" fill="#CE1E5B"></path><path d="M82.507541,217.270557 C94.312918,213.434754 109.528131,208.491016 125.855475,203.186361 C122.019672,191.380984 117.075934,176.163672 111.76918,159.83423 L68.4191475,173.924721 L82.507541,217.270557" fill="#392538"></path><path d="M173.847082,187.591344 C190.235279,182.267803 205.467279,177.31777 217.195016,173.507148 C213.359213,161.70177 208.413377,146.480262 203.106623,130.146623 L159.75659,144.237115 L173.847082,187.591344" fill="#BB242A"></path><path d="M210.484459,74.7058361 C222.457705,70.8154754 229.010885,57.954623 225.120525,45.9792787 C221.230164,34.0060328 208.369311,27.4528525 196.393967,31.3432131 L15.028459,90.292459 C3.84209836,94.3380984 -2.44459016,106.422557 0.896,118.022295 C4.37718033,130.121443 17.227541,137.49718 29.1126557,133.636197 C29.5638033,133.489311 210.484459,74.7058361 210.484459,74.7058361" fill="#72C5CD"></path><path d="M52.8220328,125.933115 C64.6274098,122.097311 79.8468197,117.151475 96.1762623,111.84682 C90.8527213,95.4565246 85.9026885,80.2245246 82.0920656,68.4946885 L38.731541,82.5872787 L52.8220328,125.933115" fill="#248C73"></path><path d="M144.159475,96.256 C160.551869,90.9303607 175.785967,85.9803279 187.515803,82.1676066 C182.190164,65.7752131 177.240131,50.5390164 173.42741,38.807082 L130.068984,52.8996721 L144.159475,96.256" fill="#62803A"></path></g></svg>'''
109 valid_events = [
105 valid_events = [
110 events.PullRequestCloseEvent,
106 events.PullRequestCloseEvent,
111 events.PullRequestMergeEvent,
107 events.PullRequestMergeEvent,
@@ -221,8 +217,23 b' class SlackIntegrationType(IntegrationTy'
221 )
217 )
222
218
223 def format_repo_push_event(self, data):
219 def format_repo_push_event(self, data):
220 branch_data = {branch['name']: branch
221 for branch in data['push']['branches']}
222
223 branches_commits = {}
224 for commit in data['push']['commits']:
225 log.critical(commit)
226 if commit['branch'] not in branches_commits:
227 branch_commits = {'branch': branch_data[commit['branch']],
228 'commits': []}
229 branches_commits[commit['branch']] = branch_commits
230
231 branch_commits = branches_commits[commit['branch']]
232 branch_commits['commits'].append(commit)
233
224 result = repo_push_template.render(
234 result = repo_push_template.render(
225 data=data,
235 data=data,
236 branches_commits=branches_commits,
226 html_to_slack_links=html_to_slack_links,
237 html_to_slack_links=html_to_slack_links,
227 )
238 )
228 return result
239 return result
@@ -28,19 +28,19 b' from celery.task import task'
28 from mako.template import Template
28 from mako.template import Template
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.translation import lazy_ugettext
31 from rhodecode.translation import _
32 from rhodecode.integrations.types.base import IntegrationTypeBase
32 from rhodecode.integrations.types.base import IntegrationTypeBase
33 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
34
33
35 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
36
35
37
36
38 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
37 class WebhookSettingsSchema(colander.Schema):
39 url = colander.SchemaNode(
38 url = colander.SchemaNode(
40 colander.String(),
39 colander.String(),
41 title=lazy_ugettext('Webhook URL'),
40 title=_('Webhook URL'),
42 description=lazy_ugettext('URL of the webhook to receive POST event.'),
41 description=_('URL of the webhook to receive POST event.'),
43 default='',
42 missing=colander.required,
43 required=True,
44 validator=colander.url,
44 validator=colander.url,
45 widget=deform.widget.TextInputWidget(
45 widget=deform.widget.TextInputWidget(
46 placeholder='https://www.example.com/webhook'
46 placeholder='https://www.example.com/webhook'
@@ -48,18 +48,24 b' class WebhookSettingsSchema(IntegrationS'
48 )
48 )
49 secret_token = colander.SchemaNode(
49 secret_token = colander.SchemaNode(
50 colander.String(),
50 colander.String(),
51 title=lazy_ugettext('Secret Token'),
51 title=_('Secret Token'),
52 description=lazy_ugettext('String used to validate received payloads.'),
52 description=_('String used to validate received payloads.'),
53 default='',
53 default='',
54 missing='',
54 widget=deform.widget.TextInputWidget(
55 widget=deform.widget.TextInputWidget(
55 placeholder='secret_token'
56 placeholder='secret_token'
56 ),
57 ),
57 )
58 )
58
59
59
60
61
62
60 class WebhookIntegrationType(IntegrationTypeBase):
63 class WebhookIntegrationType(IntegrationTypeBase):
61 key = 'webhook'
64 key = 'webhook'
62 display_name = lazy_ugettext('Webhook')
65 display_name = _('Webhook')
66 description = _('Post json events to a webhook endpoint')
67 icon = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg viewBox="0 0 256 239" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid"><g><path d="M119.540432,100.502743 C108.930124,118.338815 98.7646301,135.611455 88.3876025,152.753617 C85.7226696,157.154315 84.4040417,160.738531 86.5332204,166.333309 C92.4107024,181.787152 84.1193605,196.825836 68.5350381,200.908244 C53.8383677,204.759349 39.5192953,195.099955 36.6032893,179.365384 C34.0194114,165.437749 44.8274148,151.78491 60.1824106,149.608284 C61.4694072,149.424428 62.7821041,149.402681 64.944891,149.240571 C72.469175,136.623655 80.1773157,123.700312 88.3025935,110.073173 C73.611854,95.4654658 64.8677898,78.3885437 66.803227,57.2292132 C68.1712787,42.2715849 74.0527146,29.3462646 84.8033863,18.7517722 C105.393354,-1.53572199 136.805164,-4.82141828 161.048542,10.7510424 C184.333097,25.7086706 194.996783,54.8450075 185.906752,79.7822957 C179.052655,77.9239597 172.151111,76.049808 164.563565,73.9917997 C167.418285,60.1274266 165.306899,47.6765751 155.95591,37.0109123 C149.777932,29.9690049 141.850349,26.2780332 132.835442,24.9178894 C114.764113,22.1877169 97.0209573,33.7983633 91.7563309,51.5355878 C85.7800012,71.6669027 94.8245623,88.1111998 119.540432,100.502743 L119.540432,100.502743 Z" fill="#C73A63"></path><path d="M149.841194,79.4106285 C157.316054,92.5969067 164.905578,105.982857 172.427885,119.246236 C210.44865,107.483365 239.114472,128.530009 249.398582,151.063322 C261.81978,178.282014 253.328765,210.520191 228.933162,227.312431 C203.893073,244.551464 172.226236,241.605803 150.040866,219.46195 C155.694953,214.729124 161.376716,209.974552 167.44794,204.895759 C189.360489,219.088306 208.525074,218.420096 222.753207,201.614016 C234.885769,187.277151 234.622834,165.900356 222.138374,151.863988 C207.730339,135.66681 188.431321,135.172572 165.103273,150.721309 C155.426087,133.553447 145.58086,116.521995 136.210101,99.2295848 C133.05093,93.4015266 129.561608,90.0209366 122.440622,88.7873178 C110.547271,86.7253555 102.868785,76.5124151 102.408155,65.0698097 C101.955433,53.7537294 108.621719,43.5249733 119.04224,39.5394355 C129.363912,35.5914599 141.476705,38.7783085 148.419765,47.554004 C154.093621,54.7244134 155.896602,62.7943365 152.911402,71.6372484 C152.081082,74.1025091 151.00562,76.4886916 149.841194,79.4106285 L149.841194,79.4106285 Z" fill="#4B4B4B"></path><path d="M167.706921,187.209935 L121.936499,187.209935 C117.54964,205.253587 108.074103,219.821756 91.7464461,229.085759 C79.0544063,236.285822 65.3738898,238.72736 50.8136292,236.376762 C24.0061432,232.053165 2.08568567,207.920497 0.156179306,180.745298 C-2.02835403,149.962159 19.1309765,122.599149 47.3341915,116.452801 C49.2814904,123.524363 51.2485589,130.663141 53.1958579,137.716911 C27.3195169,150.919004 18.3639187,167.553089 25.6054984,188.352614 C31.9811726,206.657224 50.0900643,216.690262 69.7528413,212.809503 C89.8327554,208.847688 99.9567329,192.160226 98.7211371,165.37844 C117.75722,165.37844 136.809118,165.180745 155.847178,165.475311 C163.280522,165.591951 169.019617,164.820939 174.620326,158.267339 C183.840836,147.48306 200.811003,148.455721 210.741239,158.640984 C220.88894,169.049642 220.402609,185.79839 209.663799,195.768166 C199.302587,205.38802 182.933414,204.874012 173.240413,194.508846 C171.247644,192.37176 169.677943,189.835329 167.706921,187.209935 L167.706921,187.209935 Z" fill="#4A4A4A"></path></g></svg>'''
68
63 valid_events = [
69 valid_events = [
64 events.PullRequestCloseEvent,
70 events.PullRequestCloseEvent,
65 events.PullRequestMergeEvent,
71 events.PullRequestMergeEvent,
@@ -18,23 +18,29 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
22 import logging
23 import pylons
21 import pylons
24 import deform
22 import deform
23 import logging
24 import colander
25 import peppercorn
26 import webhelpers.paginate
25
27
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
28 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
27 from pyramid.renderers import render
29 from pyramid.renderers import render
28 from pyramid.response import Response
30 from pyramid.response import Response
29
31
30 from rhodecode.lib import auth
32 from rhodecode.lib import auth
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.model.db import Repository, Session, Integration
34 from rhodecode.lib.utils2 import safe_int
35 from rhodecode.lib.helpers import Page
36 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
33 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.integration import IntegrationModel
38 from rhodecode.model.integration import IntegrationModel
35 from rhodecode.admin.navigation import navigation_list
39 from rhodecode.admin.navigation import navigation_list
36 from rhodecode.translation import _
40 from rhodecode.translation import _
37 from rhodecode.integrations import integration_type_registry
41 from rhodecode.integrations import integration_type_registry
42 from rhodecode.model.validation_schema.schemas.integration_schema import (
43 make_integration_schema, IntegrationScopeType)
38
44
39 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
40
46
@@ -59,28 +65,51 b' class IntegrationSettingsViewBase(object'
59
65
60 self.IntegrationType = None
66 self.IntegrationType = None
61 self.repo = None
67 self.repo = None
68 self.repo_group = None
62 self.integration = None
69 self.integration = None
63 self.integrations = {}
70 self.integrations = {}
64
71
65 request = self.request
72 request = self.request
66
73
67 if 'repo_name' in request.matchdict: # we're in a repo context
74 if 'repo_name' in request.matchdict: # in repo settings context
68 repo_name = request.matchdict['repo_name']
75 repo_name = request.matchdict['repo_name']
69 self.repo = Repository.get_by_repo_name(repo_name)
76 self.repo = Repository.get_by_repo_name(repo_name)
70
77
71 if 'integration' in request.matchdict: # we're in integration context
78 if 'repo_group_name' in request.matchdict: # in group settings context
79 repo_group_name = request.matchdict['repo_group_name']
80 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
81
82
83 if 'integration' in request.matchdict: # integration type context
72 integration_type = request.matchdict['integration']
84 integration_type = request.matchdict['integration']
73 self.IntegrationType = integration_type_registry[integration_type]
85 self.IntegrationType = integration_type_registry[integration_type]
74
86
75 if 'integration_id' in request.matchdict: # single integration context
87 if 'integration_id' in request.matchdict: # single integration context
76 integration_id = request.matchdict['integration_id']
88 integration_id = request.matchdict['integration_id']
77 self.integration = Integration.get(integration_id)
89 self.integration = Integration.get(integration_id)
78 else: # list integrations context
90
79 for integration in IntegrationModel().get_integrations(self.repo):
91 # extra perms check just in case
80 self.integrations.setdefault(integration.integration_type, []
92 if not self._has_perms_for_integration(self.integration):
81 ).append(integration)
93 raise HTTPForbidden()
82
94
83 self.settings = self.integration and self.integration.settings or {}
95 self.settings = self.integration and self.integration.settings or {}
96 self.admin_view = not (self.repo or self.repo_group)
97
98 def _has_perms_for_integration(self, integration):
99 perms = self.request.user.permissions
100
101 if 'hg.admin' in perms['global']:
102 return True
103
104 if integration.repo:
105 return perms['repositories'].get(
106 integration.repo.repo_name) == 'repository.admin'
107
108 if integration.repo_group:
109 return perms['repositories_groups'].get(
110 integration.repo_group.group_name) == 'group.admin'
111
112 return False
84
113
85 def _template_c_context(self):
114 def _template_c_context(self):
86 # TODO: dan: this is a stopgap in order to inherit from current pylons
115 # TODO: dan: this is a stopgap in order to inherit from current pylons
@@ -91,7 +120,10 b' class IntegrationSettingsViewBase(object'
91 c.active = 'integrations'
120 c.active = 'integrations'
92 c.rhodecode_user = self.request.user
121 c.rhodecode_user = self.request.user
93 c.repo = self.repo
122 c.repo = self.repo
123 c.repo_group = self.repo_group
94 c.repo_name = self.repo and self.repo.repo_name or None
124 c.repo_name = self.repo and self.repo.repo_name or None
125 c.repo_group_name = self.repo_group and self.repo_group.group_name or None
126
95 if self.repo:
127 if self.repo:
96 c.repo_info = self.repo
128 c.repo_info = self.repo
97 c.rhodecode_db_repo = self.repo
129 c.rhodecode_db_repo = self.repo
@@ -102,34 +134,77 b' class IntegrationSettingsViewBase(object'
102 return c
134 return c
103
135
104 def _form_schema(self):
136 def _form_schema(self):
105 if self.integration:
137 schema = make_integration_schema(IntegrationType=self.IntegrationType,
106 settings = self.integration.settings
138 settings=self.settings)
107 else:
108 settings = {}
109 return self.IntegrationType(settings=settings).settings_schema()
110
139
111 def settings_get(self, defaults=None, errors=None, form=None):
140 # returns a clone, important if mutating the schema later
112 """
141 return schema.bind(
113 View that displays the plugin settings as a form.
142 permissions=self.request.user.permissions,
114 """
143 no_scope=not self.admin_view)
115 defaults = defaults or {}
144
116 errors = errors or {}
145
146 def _form_defaults(self):
147 defaults = {}
117
148
118 if self.integration:
149 if self.integration:
119 defaults = self.integration.settings or {}
150 defaults['settings'] = self.integration.settings or {}
120 defaults['name'] = self.integration.name
151 defaults['options'] = {
121 defaults['enabled'] = self.integration.enabled
152 'name': self.integration.name,
153 'enabled': self.integration.enabled,
154 'scope': {
155 'repo': self.integration.repo,
156 'repo_group': self.integration.repo_group,
157 'child_repos_only': self.integration.child_repos_only,
158 },
159 }
122 else:
160 else:
123 if self.repo:
161 if self.repo:
124 scope = self.repo.repo_name
162 scope = _('{repo_name} repository').format(
163 repo_name=self.repo.repo_name)
164 elif self.repo_group:
165 scope = _('{repo_group_name} repo group').format(
166 repo_group_name=self.repo_group.group_name)
125 else:
167 else:
126 scope = _('Global')
168 scope = _('Global')
127
169
128 defaults['name'] = '{} {} integration'.format(scope,
170 defaults['options'] = {
129 self.IntegrationType.display_name)
171 'enabled': True,
130 defaults['enabled'] = True
172 'name': _('{name} integration').format(
173 name=self.IntegrationType.display_name),
174 }
175 defaults['options']['scope'] = {
176 'repo': self.repo,
177 'repo_group': self.repo_group,
178 }
179
180 return defaults
131
181
132 schema = self._form_schema().bind(request=self.request)
182 def _delete_integration(self, integration):
183 Session().delete(self.integration)
184 Session().commit()
185 self.request.session.flash(
186 _('Integration {integration_name} deleted successfully.').format(
187 integration_name=self.integration.name),
188 queue='success')
189
190 if self.repo:
191 redirect_to = self.request.route_url(
192 'repo_integrations_home', repo_name=self.repo.repo_name)
193 elif self.repo_group:
194 redirect_to = self.request.route_url(
195 'repo_group_integrations_home',
196 repo_group_name=self.repo_group.group_name)
197 else:
198 redirect_to = self.request.route_url('global_integrations_home')
199 raise HTTPFound(redirect_to)
200
201 def settings_get(self, defaults=None, form=None):
202 """
203 View that displays the integration settings as a form.
204 """
205
206 defaults = defaults or self._form_defaults()
207 schema = self._form_schema()
133
208
134 if self.integration:
209 if self.integration:
135 buttons = ('submit', 'delete')
210 buttons = ('submit', 'delete')
@@ -138,23 +213,10 b' class IntegrationSettingsViewBase(object'
138
213
139 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
214 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
140
215
141 for node in schema:
142 setting = self.settings.get(node.name)
143 if setting is not None:
144 defaults.setdefault(node.name, setting)
145 else:
146 if node.default:
147 defaults.setdefault(node.name, node.default)
148
149 template_context = {
216 template_context = {
150 'form': form,
217 'form': form,
151 'defaults': defaults,
152 'errors': errors,
153 'schema': schema,
154 'current_IntegrationType': self.IntegrationType,
218 'current_IntegrationType': self.IntegrationType,
155 'integration': self.integration,
219 'integration': self.integration,
156 'settings': self.settings,
157 'resource': self.context,
158 'c': self._template_c_context(),
220 'c': self._template_c_context(),
159 }
221 }
160
222
@@ -163,71 +225,93 b' class IntegrationSettingsViewBase(object'
163 @auth.CSRFRequired()
225 @auth.CSRFRequired()
164 def settings_post(self):
226 def settings_post(self):
165 """
227 """
166 View that validates and stores the plugin settings.
228 View that validates and stores the integration settings.
167 """
229 """
168 if self.request.params.get('delete'):
230 controls = self.request.POST.items()
169 Session().delete(self.integration)
231 pstruct = peppercorn.parse(controls)
170 Session().commit()
232
171 self.request.session.flash(
233 if self.integration and pstruct.get('delete'):
172 _('Integration {integration_name} deleted successfully.').format(
234 return self._delete_integration(self.integration)
173 integration_name=self.integration.name),
235
174 queue='success')
236 schema = self._form_schema()
175 if self.repo:
237
176 redirect_to = self.request.route_url(
238 skip_settings_validation = False
177 'repo_integrations_home', repo_name=self.repo.repo_name)
239 if self.integration and 'enabled' not in pstruct.get('options', {}):
178 else:
240 skip_settings_validation = True
179 redirect_to = self.request.route_url('global_integrations_home')
241 schema['settings'].validator = None
180 raise HTTPFound(redirect_to)
242 for field in schema['settings'].children:
243 field.validator = None
244 field.missing = ''
181
245
182 schema = self._form_schema().bind(request=self.request)
246 if self.integration:
247 buttons = ('submit', 'delete')
248 else:
249 buttons = ('submit',)
183
250
184 form = deform.Form(schema, buttons=('submit', 'delete'))
251 form = deform.Form(schema, buttons=buttons)
185
252
186 params = {}
253 if not self.admin_view:
187 for node in schema.children:
254 # scope is read only field in these cases, and has to be added
188 if type(node.typ) in (colander.Set, colander.List):
255 options = pstruct.setdefault('options', {})
189 val = self.request.params.getall(node.name)
256 if 'scope' not in options:
190 else:
257 options['scope'] = IntegrationScopeType().serialize(None, {
191 val = self.request.params.get(node.name)
258 'repo': self.repo,
192 if val:
259 'repo_group': self.repo_group,
193 params[node.name] = val
260 })
194
261
195 controls = self.request.POST.items()
196 try:
262 try:
197 valid_data = form.validate(controls)
263 valid_data = form.validate_pstruct(pstruct)
198 except deform.ValidationFailure as e:
264 except deform.ValidationFailure as e:
199 self.request.session.flash(
265 self.request.session.flash(
200 _('Errors exist when saving integration settings. '
266 _('Errors exist when saving integration settings. '
201 'Please check the form inputs.'),
267 'Please check the form inputs.'),
202 queue='error')
268 queue='error')
203 return self.settings_get(errors={}, defaults=params, form=e)
269 return self.settings_get(form=e)
204
270
205 if not self.integration:
271 if not self.integration:
206 self.integration = Integration()
272 self.integration = Integration()
207 self.integration.integration_type = self.IntegrationType.key
273 self.integration.integration_type = self.IntegrationType.key
208 if self.repo:
209 self.integration.repo = self.repo
210 Session().add(self.integration)
274 Session().add(self.integration)
211
275
212 self.integration.enabled = valid_data.pop('enabled', False)
276 scope = valid_data['options']['scope']
213 self.integration.name = valid_data.pop('name')
214 self.integration.settings = valid_data
215
277
278 IntegrationModel().update_integration(self.integration,
279 name=valid_data['options']['name'],
280 enabled=valid_data['options']['enabled'],
281 settings=valid_data['settings'],
282 repo=scope['repo'],
283 repo_group=scope['repo_group'],
284 child_repos_only=scope['child_repos_only'],
285 )
286
287
288 self.integration.settings = valid_data['settings']
216 Session().commit()
289 Session().commit()
217
218 # Display success message and redirect.
290 # Display success message and redirect.
219 self.request.session.flash(
291 self.request.session.flash(
220 _('Integration {integration_name} updated successfully.').format(
292 _('Integration {integration_name} updated successfully.').format(
221 integration_name=self.IntegrationType.display_name),
293 integration_name=self.IntegrationType.display_name),
222 queue='success')
294 queue='success')
223
295
224 if self.repo:
296
225 redirect_to = self.request.route_url(
297 # if integration scope changes, we must redirect to the right place
226 'repo_integrations_edit', repo_name=self.repo.repo_name,
298 # keeping in mind if the original view was for /repo/ or /_admin/
299 admin_view = not (self.repo or self.repo_group)
300
301 if self.integration.repo and not admin_view:
302 redirect_to = self.request.route_path(
303 'repo_integrations_edit',
304 repo_name=self.integration.repo.repo_name,
305 integration=self.integration.integration_type,
306 integration_id=self.integration.integration_id)
307 elif self.integration.repo_group and not admin_view:
308 redirect_to = self.request.route_path(
309 'repo_group_integrations_edit',
310 repo_group_name=self.integration.repo_group.group_name,
227 integration=self.integration.integration_type,
311 integration=self.integration.integration_type,
228 integration_id=self.integration.integration_id)
312 integration_id=self.integration.integration_id)
229 else:
313 else:
230 redirect_to = self.request.route_url(
314 redirect_to = self.request.route_path(
231 'global_integrations_edit',
315 'global_integrations_edit',
232 integration=self.integration.integration_type,
316 integration=self.integration.integration_type,
233 integration_id=self.integration.integration_id)
317 integration_id=self.integration.integration_id)
@@ -235,31 +319,60 b' class IntegrationSettingsViewBase(object'
235 return HTTPFound(redirect_to)
319 return HTTPFound(redirect_to)
236
320
237 def index(self):
321 def index(self):
238 current_integrations = self.integrations
322 """ List integrations """
239 if self.IntegrationType:
323 if self.repo:
240 current_integrations = {
324 scope = self.repo
241 self.IntegrationType.key: self.integrations.get(
325 elif self.repo_group:
242 self.IntegrationType.key, [])
326 scope = self.repo_group
243 }
327 else:
328 scope = 'all'
329
330 integrations = []
331
332 for integration in IntegrationModel().get_integrations(
333 scope=scope, IntegrationType=self.IntegrationType):
334
335 # extra permissions check *just in case*
336 if not self._has_perms_for_integration(integration):
337 continue
338 integrations.append(integration)
339
340 sort_arg = self.request.GET.get('sort', 'name:asc')
341 if ':' in sort_arg:
342 sort_field, sort_dir = sort_arg.split(':')
343 else:
344 sort_field = sort_arg, 'asc'
345
346 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
347
348 integrations.sort(
349 key=lambda x: getattr(x[1], sort_field), reverse=(sort_dir=='desc'))
350
351
352 page_url = webhelpers.paginate.PageURL(
353 self.request.path, self.request.GET)
354 page = safe_int(self.request.GET.get('page', 1), 1)
355
356 integrations = Page(integrations, page=page, items_per_page=10,
357 url=page_url)
244
358
245 template_context = {
359 template_context = {
360 'sort_field': sort_field,
361 'rev_sort_dir': sort_dir != 'desc' and 'desc' or 'asc',
246 'current_IntegrationType': self.IntegrationType,
362 'current_IntegrationType': self.IntegrationType,
247 'current_integrations': current_integrations,
363 'integrations_list': integrations,
248 'available_integrations': integration_type_registry,
364 'available_integrations': integration_type_registry,
249 'c': self._template_c_context()
365 'c': self._template_c_context(),
366 'request': self.request,
250 }
367 }
368 return template_context
251
369
252 if self.repo:
370 def new_integration(self):
253 html = render('rhodecode:templates/admin/integrations/list.html',
371 template_context = {
254 template_context,
372 'available_integrations': integration_type_registry,
255 request=self.request)
373 'c': self._template_c_context(),
256 else:
374 }
257 html = render('rhodecode:templates/admin/integrations/list.html',
375 return template_context
258 template_context,
259 request=self.request)
260
261 return Response(html)
262
263
376
264 class GlobalIntegrationsView(IntegrationSettingsViewBase):
377 class GlobalIntegrationsView(IntegrationSettingsViewBase):
265 def perm_check(self, user):
378 def perm_check(self, user):
@@ -270,3 +383,10 b' class RepoIntegrationsView(IntegrationSe'
270 def perm_check(self, user):
383 def perm_check(self, user):
271 return auth.HasRepoPermissionAll('repository.admin'
384 return auth.HasRepoPermissionAll('repository.admin'
272 )(repo_name=self.repo.repo_name, user=user)
385 )(repo_name=self.repo.repo_name, user=user)
386
387
388 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
389 def perm_check(self, user):
390 return auth.HasRepoGroupPermissionAll('group.admin'
391 )(group_name=self.repo_group.group_name, user=user)
392
@@ -48,12 +48,12 b' def annotate_highlight('
48 :param headers: dictionary with headers (keys are whats in ``order``
48 :param headers: dictionary with headers (keys are whats in ``order``
49 parameter)
49 parameter)
50 """
50 """
51 from rhodecode.lib.utils import get_custom_lexer
51 from rhodecode.lib.helpers import get_lexer_for_filenode
52 options['linenos'] = True
52 options['linenos'] = True
53 formatter = AnnotateHtmlFormatter(
53 formatter = AnnotateHtmlFormatter(
54 filenode=filenode, order=order, headers=headers,
54 filenode=filenode, order=order, headers=headers,
55 annotate_from_commit_func=annotate_from_commit_func, **options)
55 annotate_from_commit_func=annotate_from_commit_func, **options)
56 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
56 lexer = get_lexer_for_filenode(filenode)
57 highlighted = highlight(filenode.content, lexer, formatter)
57 highlighted = highlight(filenode.content, lexer, formatter)
58 return highlighted
58 return highlighted
59
59
@@ -1116,9 +1116,11 b' class CSRFRequired(object):'
1116 For use with the ``webhelpers.secure_form`` helper functions.
1116 For use with the ``webhelpers.secure_form`` helper functions.
1117
1117
1118 """
1118 """
1119 def __init__(self, token=csrf_token_key, header='X-CSRF-Token'):
1119 def __init__(self, token=csrf_token_key, header='X-CSRF-Token',
1120 except_methods=None):
1120 self.token = token
1121 self.token = token
1121 self.header = header
1122 self.header = header
1123 self.except_methods = except_methods or []
1122
1124
1123 def __call__(self, func):
1125 def __call__(self, func):
1124 return get_cython_compat_decorator(self.__wrapper, func)
1126 return get_cython_compat_decorator(self.__wrapper, func)
@@ -1131,6 +1133,9 b' class CSRFRequired(object):'
1131 return supplied_token and supplied_token == cur_token
1133 return supplied_token and supplied_token == cur_token
1132
1134
1133 def __wrapper(self, func, *fargs, **fkwargs):
1135 def __wrapper(self, func, *fargs, **fkwargs):
1136 if request.method in self.except_methods:
1137 return func(*fargs, **fkwargs)
1138
1134 cur_token = get_csrf_token(save_if_missing=False)
1139 cur_token = get_csrf_token(save_if_missing=False)
1135 if self.check_csrf(request, cur_token):
1140 if self.check_csrf(request, cur_token):
1136 if request.POST.get(self.token):
1141 if request.POST.get(self.token):
@@ -28,6 +28,7 b' import logging'
28 import socket
28 import socket
29
29
30 import ipaddress
30 import ipaddress
31 import pyramid.threadlocal
31
32
32 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
@@ -276,7 +277,7 b' def attach_context_attributes(context, r'
276 # Visual options
277 # Visual options
277 context.visual = AttributeDict({})
278 context.visual = AttributeDict({})
278
279
279 # DB store
280 # DB stored Visual Items
280 context.visual.show_public_icon = str2bool(
281 context.visual.show_public_icon = str2bool(
281 rc_config.get('rhodecode_show_public_icon'))
282 rc_config.get('rhodecode_show_public_icon'))
282 context.visual.show_private_icon = str2bool(
283 context.visual.show_private_icon = str2bool(
@@ -368,6 +369,8 b' def attach_context_attributes(context, r'
368 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(
369 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(
369 context.rhodecode_user.user_id)
370 context.rhodecode_user.user_id)
370
371
372 context.pyramid_request = pyramid.threadlocal.get_current_request()
373
371
374
372 def get_auth_user(environ):
375 def get_auth_user(environ):
373 ip_addr = get_ip_addr(environ)
376 ip_addr = get_ip_addr(environ)
@@ -84,6 +84,7 b' def get_user_data(user_id):'
84 'icon_link': h.gravatar_url(user.email, 14),
84 'icon_link': h.gravatar_url(user.email, 14),
85 'display_name': h.person(user, 'username_or_name_or_email'),
85 'display_name': h.person(user, 'username_or_name_or_email'),
86 'display_link': h.link_to_user(user),
86 'display_link': h.link_to_user(user),
87 'notifications': user.user_data.get('notification_status', True)
87 }
88 }
88
89
89
90
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/templates/admin/integrations/edit.html to rhodecode/templates/admin/integrations/form.html
NO CONTENT: file renamed from rhodecode/templates/admin/integrations/edit.html to rhodecode/templates/admin/integrations/form.html
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now