##// 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,5 +1,5 b''
1 1 [bumpversion]
2 current_version = 4.3.1
2 current_version = 4.4.0
3 3 message = release: Bump version {current_version} to {new_version}
4 4
5 5 [bumpversion:file:rhodecode/VERSION]
@@ -8,6 +8,7 b' syntax: glob'
8 8 *.swp
9 9 *.tox
10 10 *.DS_Store*
11 rhodecode/public/js/src/components/**/*.css
11 12
12 13 syntax: regexp
13 14
@@ -23,6 +24,7 b' syntax: regexp'
23 24 ^_dev
24 25 ^._dev
25 26 ^build/
27 ^bower_components/
26 28 ^coverage\.xml$
27 29 ^data$
28 30 ^\.eggs/
@@ -38,7 +40,11 b' syntax: regexp'
38 40 ^rcextensions/
39 41 ^result$
40 42 ^rhodecode/public/css/style.css$
43 ^rhodecode/public/css/style-polymer.css$
44 ^rhodecode/public/js/rhodecode-components.html$
41 45 ^rhodecode/public/js/scripts.js$
46 ^rhodecode/public/js/src/components/root-styles.gen.html$
47 ^rhodecode/public/js/vendors/webcomponentsjs/
42 48 ^rhodecode\.db$
43 49 ^rhodecode\.log$
44 50 ^rhodecode_dev\.log$
@@ -4,26 +4,21 b' done = false'
4 4 [task:bump_version]
5 5 done = true
6 6
7 [task:rc_tools_pinned]
8 done = true
9
10 7 [task:fixes_on_stable]
11 done = true
12 8
13 9 [task:pip2nix_generated]
14 done = true
15 10
16 11 [task:changelog_updated]
17 done = true
18 12
19 13 [task:generate_api_docs]
20 done = true
14
15 [task:updated_translation]
21 16
22 17 [release]
23 state = prepared
24 version = 4.3.1
18 state = in_progress
19 version = 4.4.0
25 20
26 [task:updated_translation]
21 [task:rc_tools_pinned]
27 22
28 23 [task:generate_js_routes]
29 24
@@ -1,144 +1,15 b''
1 module.exports = function(grunt) {
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',
1 var gruntConfig = require('./grunt_config.json');
65 2
66 // Rhodecode components
67 '<%= dirs.js.src %>/rhodecode/init.js',
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 });
3 module.exports = function(grunt) {
4 grunt.initConfig(gruntConfig);
137 5
138 6 grunt.loadNpmTasks('grunt-contrib-less');
139 7 grunt.loadNpmTasks('grunt-contrib-concat');
140 8 grunt.loadNpmTasks('grunt-contrib-watch');
141 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 29 # 502 page
30 30 include rhodecode/public/502.html
31 31
32 # 502 page
33 include rhodecode/public/502.html
34
32 35 # images, css
33 36 include rhodecode/public/css/*.css
34 37 include rhodecode/public/images/*.*
@@ -414,7 +414,7 b' search.location = %(here)s/data/index'
414 414 ## channelstream enables persistent connections and live notification
415 415 ## in the system. It's also used by the chat system
416 416
417 channelstream.enabled = true
417 channelstream.enabled = false
418 418 ## location of channelstream server on the backend
419 419 channelstream.server = 127.0.0.1:9800
420 420 ## location of the channelstream server from outside world
@@ -388,7 +388,7 b' search.location = %(here)s/data/index'
388 388 ## channelstream enables persistent connections and live notification
389 389 ## in the system. It's also used by the chat system
390 390
391 channelstream.enabled = true
391 channelstream.enabled = false
392 392 ## location of channelstream server on the backend
393 393 channelstream.server = 127.0.0.1:9800
394 394 ## location of the channelstream server from outside world
@@ -30,6 +30,10 b' let'
30 30 then pythonPackages
31 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 37 elem = builtins.elem;
34 38 basename = path: with pkgs.lib; last (splitString "/" path);
35 39 startsWith = prefix: full: let
@@ -41,31 +45,28 b' let'
41 45 ext = last (splitString "." path);
42 46 in
43 47 !elem (basename path) [
44 ".git" ".hg" "__pycache__" ".eggs" "node_modules"
45 "build" "data" "tmp"] &&
48 ".git" ".hg" "__pycache__" ".eggs"
49 "bower_components" "node_modules"
50 "build" "data" "result" "tmp"] &&
46 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 54 !startsWith "result" path;
48 55
49 56 sources = pkgs.config.rc.sources or {};
57 version = builtins.readFile ./rhodecode/VERSION;
50 58 rhodecode-enterprise-ce-src = builtins.filterSource src-filter ./.;
51 59
52 # Load the generated node packages
53 nodePackages = pkgs.callPackage "${pkgs.path}/pkgs/top-level/node-packages.nix" rec {
54 self = nodePackages;
55 generated = pkgs.callPackage ./pkgs/node-packages.nix { inherit self; };
60 nodeEnv = import ./pkgs/node-default.nix {
61 inherit pkgs;
56 62 };
63 nodeDependencies = nodeEnv.shell.nodeDependencies;
57 64
58 # TODO: Should be taken automatically out of the generates packages.
59 # apps.nix has one solution for this, although I'd prefer to have the deps
60 # from package.json mapped in here.
61 nodeDependencies = with nodePackages; [
62 grunt
63 grunt-contrib-concat
64 grunt-contrib-jshint
65 grunt-contrib-less
66 grunt-contrib-watch
67 jshint
68 ];
65 bowerComponents = buildBowerComponents {
66 name = "enterprise-ce-${version}";
67 generated = ./pkgs/bower-packages.nix;
68 src = rhodecode-enterprise-ce-src;
69 };
69 70
70 71 pythonGeneratedPackages = self: basePythonPackages.override (a: {
71 72 inherit self;
@@ -86,16 +87,25 b' let'
86 87 pythonLocalOverrides = self: super: {
87 88 rhodecode-enterprise-ce =
88 89 let
89 version = builtins.readFile ./rhodecode/VERSION;
90 linkNodeModules = ''
90 linkNodeAndBowerPackages = ''
91 echo "Export RhodeCode CE path"
92 export RHODECODE_CE_PATH=${rhodecode-enterprise-ce-src}
91 93 echo "Link node packages"
92 # TODO: check if this adds stuff as a dependency, closure size
93 94 rm -fr node_modules
94 mkdir -p node_modules
95 ${pkgs.lib.concatMapStrings (dep: ''
96 ln -sfv ${dep}/lib/node_modules/${dep.pkgName} node_modules/
97 '') nodeDependencies}
95 mkdir node_modules
96 # johbo: Linking individual packages allows us to run "npm install"
97 # inside of a shell to try things out. Re-entering the shell will
98 # restore a clean environment.
99 ln -s ${nodeDependencies}/lib/node_modules/* node_modules/
100
98 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 110 in super.rhodecode-enterprise-ce.override (attrs: {
101 111
@@ -109,6 +119,7 b' let'
109 119 buildInputs =
110 120 attrs.buildInputs ++
111 121 (with self; [
122 pkgs.nodePackages.bower
112 123 pkgs.nodePackages.grunt-cli
113 124 pkgs.subversion
114 125 pytest-catchlog
@@ -123,7 +134,8 b' let'
123 134 # pkgs/default.nix?
124 135 passthru = {
125 136 inherit
126 linkNodeModules
137 bowerComponents
138 linkNodeAndBowerPackages
127 139 myPythonPackagesUnfix
128 140 pythonLocalOverrides;
129 141 pythonPackages = self;
@@ -145,7 +157,7 b' let'
145 157 export PYTHONPATH="$tmp_path/${self.python.sitePackages}:$PYTHONPATH"
146 158 mkdir -p $tmp_path/${self.python.sitePackages}
147 159 python setup.py develop --prefix $tmp_path --allow-hosts ""
148 '' + linkNodeModules;
160 '' + linkNodeAndBowerPackages;
149 161
150 162 preCheck = ''
151 163 export PATH="$out/bin:$PATH"
@@ -156,7 +168,7 b' let'
156 168 rm -rf $out/lib/${self.python.libPrefix}/site-packages/rhodecode/tests
157 169 '';
158 170
159 preBuild = linkNodeModules + ''
171 preBuild = linkNodeAndBowerPackages + ''
160 172 grunt
161 173 rm -fr node_modules
162 174 '';
@@ -29,13 +29,3 b' 4. You will see the labs setting on the'
29 29 :menuselection:`Admin --> Settings --> labs` page.
30 30
31 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 26 * :ref:`vcs-server-versions`
27 27 * :ref:`vcs-server-maintain`
28 28 * :ref:`vcs-server-config-file`
29 * :ref:`svn-http`
29 30
30 31 .. _install-vcs:
31 32
@@ -297,5 +298,133 b' For a more detailed explanation of the l'
297 298 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
298 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 18 db-schema
19 19 dev-settings
20 20 api
21 dependencies
@@ -111,15 +111,18 b' time operation::'
111 111 Compile CSS and JavaScript
112 112 ^^^^^^^^^^^^^^^^^^^^^^^^^^
113 113
114 To use the application's frontend, you will need to compile the CSS and
115 JavaScript with Grunt. This is easily done from within the nix-shell using the
116 following command::
114 To use the application's frontend and prepare it for production deployment,
115 you will need to compile the CSS and JavaScript with Grunt.
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
121 files.
123 grunt watch
122 124
125 This prepares the development (with comments/whitespace) versions of files.
123 126
124 127 Start the Development Server
125 128 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,6 +45,11 b' JavaScript'
45 45 ----------
46 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 54 HTML
50 55 ----
@@ -14,9 +14,6 b' py.test based test suite'
14 14 The test suite is in the folder :file:`rhodecode/tests/` and should be run with
15 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 17 py.test rhodecode
21 18
22 19
@@ -26,20 +23,28 b' py.test integration'
26 23
27 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
30 Pylons web framework. It sets up the Pylons environment based on the given ini
31 file.
26 - :file:`rhodecode/tests/pylons_plugin.py` is a py.test plugin which does the
27 integration with the Pylons web framework. It sets up the Pylons environment
28 based on the given ini file.
32 29
33 30 Tests which depend on the Pylons environment to be set up must request the
34 31 fixture `pylonsapp`.
35 32
36 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 37 - :file:`conftest.py` plugins are used to provide a special integration for
40 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 49 VCS backend selection
45 50 ---------------------
@@ -19,8 +19,7 b' Quick Start Installation Guide'
19 19
20 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
23 or main page.
22 1. Download the latest |RCC| installer from `rhodecode.com/download`_.
24 23 If you don't have an account, sign up at `rhodecode.com/register`_.
25 24
26 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 106 .. _rhodecode.com/download/: https://rhodecode.com/download/
108 107 .. _rhodecode.com: https://rhodecode.com/
109 108 .. _rhodecode.com/register: https://rhodecode.com/register/
109 .. _rhodecode.com/download: https://rhodecode.com/download/
110
@@ -3,20 +3,20 b''
3 3 PostgreSQL
4 4 ----------
5 5
6 To use a PostgreSQL database you should install and configurevthe database
7 before installing |RCV|. This is becausevduring |RCV| installation you will
8 setup a connection to your PostgreSQL database. To work with PostgreSQL,
6 To use a PostgreSQL database, you should install and configure the database
7 before installing |RCV|. This is because during |RCV| installation you will
8 setup the connection to your PostgreSQL database. To work with PostgreSQL,
9 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 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 14 with |RCV|.
15 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 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 20 * Your database username
21 21 * Your database password
22 22 * A new database name
@@ -9,6 +9,7 b' Release Notes'
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.4.0.rst
12 13 release-notes-4.3.1.rst
13 14 release-notes-4.3.0.rst
14 15 release-notes-4.2.1.rst
@@ -3,10 +3,16 b''
3 3 "version": "0.0.1",
4 4 "devDependencies": {
5 5 "grunt": "^0.4.5",
6 "grunt-contrib-copy": "^1.0.0",
6 7 "grunt-contrib-concat": "^0.5.1",
7 8 "grunt-contrib-jshint": "^0.12.0",
8 9 "grunt-contrib-less": "^1.1.0",
9 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
The requested commit or file is too big and content was truncated. Show full diff
@@ -14,6 +14,20 b' let'
14 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 31 in
18 32
19 33 self: super: {
@@ -96,7 +110,7 b' self: super: {'
96 110 });
97 111
98 112 py-gfm = super.py-gfm.override {
99 src = pkgs.fetchgit {
113 src = backwardsCompatibleFetchgit {
100 114 url = "https://code.rhodecode.com/upstream/py-gfm";
101 115 rev = "0d66a19bc16e3d49de273c0f797d4e4781e8c0f2";
102 116 sha256 = "0ryp74jyihd3ckszq31bml5jr3bciimhfp7va7kw6ld92930ksv3";
@@ -120,7 +134,7 b' self: super: {'
120 134
121 135 Pylons = super.Pylons.override (attrs: {
122 136 name = "Pylons-1.0.1-patch1";
123 src = pkgs.fetchgit {
137 src = backwardsCompatibleFetchgit {
124 138 url = "https://code.rhodecode.com/upstream/pylons";
125 139 rev = "707354ee4261b9c10450404fc9852ccea4fd667d";
126 140 sha256 = "b2763274c2780523a335f83a1df65be22ebe4ff413a7bc9e9288d23c1f62032e";
@@ -51,19 +51,6 b''
51 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 54 FormEncode = super.buildPythonPackage {
68 55 name = "FormEncode-1.2.4";
69 56 buildInputs = with self; [];
@@ -1430,7 +1417,7 b''
1430 1417 };
1431 1418 };
1432 1419 rhodecode-enterprise-ce = super.buildPythonPackage {
1433 name = "rhodecode-enterprise-ce-4.3.1";
1420 name = "rhodecode-enterprise-ce-4.4.0";
1434 1421 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1435 1422 doCheck = true;
1436 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 1 Babel==1.3
2 2 Beaker==1.7.0
3 3 CProfileV==1.0.6
4 Fabric==1.10.0
5 4 FormEncode==1.2.4
6 5 Jinja2==2.7.3
7 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 51 EXTENSIONS = {}
52 52
53 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 55 __platform__ = platform.system()
56 56 __license__ = 'AGPLv3, and Commercial License'
57 57 __author__ = 'RhodeCode GmbH'
@@ -60,3 +60,4 b' EXTENSIONS = {}'
60 60 is_windows = __platform__ in ['Windows']
61 61 is_unix = not is_windows
62 62 is_test = False
63 disable_error_handler = False
@@ -29,7 +29,9 b' from rhodecode.lib.ext_json import json'
29 29 def url_gen(request):
30 30 urls = {
31 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 36 return json.dumps(urls)
35 37
@@ -95,6 +95,7 b' class ChannelstreamView(object):'
95 95 'display_name': None,
96 96 'display_link': None,
97 97 }
98 user_data['permissions'] = c.rhodecode_user.permissions
98 99 payload = {
99 100 'username': user.username,
100 101 'user_state': user_data,
@@ -28,7 +28,11 b' from rhodecode.lib.utils2 import __get_l'
28 28
29 29 # language map is also used by whoosh indexer, which for those specified
30 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 37 DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
34 38
@@ -158,6 +158,8 b' def load_pyramid_environment(global_conf'
158 158 # This has to be done before the database connection is initialized.
159 159 if settings['is_test']:
160 160 rhodecode.is_test = True
161 rhodecode.disable_error_handler = True
162
161 163 utils.initialize_test_environment(settings_merged)
162 164
163 165 # Initialize the database connection.
@@ -44,9 +44,10 b' from rhodecode.config import patches'
44 44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 45 from rhodecode.config.environment import (
46 46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.vcs.exceptions import VCSCommunicationError
47 49 from rhodecode.lib.middleware import csrf
48 50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
50 51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 52 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
@@ -193,10 +194,6 b' def make_not_found_view(config):'
193 194
194 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 197 def pylons_app_with_error_handler(context, request):
201 198 """
202 199 Handle exceptions from rc pylons app:
@@ -221,10 +218,18 b' def make_not_found_view(config):'
221 218 return error_handler(response, request)
222 219 except HTTPError as e: # pyramid type exceptions
223 220 return error_handler(e, request)
224 except Exception:
225 if settings.get('debugtoolbar.enabled', False):
221 except Exception as e:
222 log.exception(e)
223
224 if (settings.get('debugtoolbar.enabled', False) or
225 rhodecode.disable_error_handler):
226 226 raise
227
228 if isinstance(e, VCSCommunicationError):
229 return error_handler(VCSServerUnavailable(), request)
230
227 231 return error_handler(HTTPInternalServerError(), request)
232
228 233 return response
229 234
230 235 return pylons_app_with_error_handler
@@ -249,7 +254,6 b' def webob_to_pyramid_http_response(webob'
249 254
250 255
251 256 def error_handler(exception, request):
252 # TODO: dan: replace the old pylons error controller with this
253 257 from rhodecode.model.settings import SettingsModel
254 258 from rhodecode.lib.utils2 import AttributeDict
255 259
@@ -278,6 +282,10 b' def error_handler(exception, request):'
278 282 if not c.rhodecode_name:
279 283 c.rhodecode_name = 'Rhodecode'
280 284
285 c.causes = []
286 if hasattr(base_response, 'causes'):
287 c.causes = base_response.causes
288
281 289 response = render_to_response(
282 290 '/errors/error_document.html', {'c': c}, request=request,
283 291 response=base_response)
@@ -42,6 +42,7 b" STATIC_FILE_PREFIX = '/_static'"
42 42 URL_NAME_REQUIREMENTS = {
43 43 # group name can have a slash in them, but they must not end with a slash
44 44 'group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
45 46 # repo names can have a slash in them, but they must not end with a slash
46 47 'repo_name': r'.*?[^/]',
47 48 # file path eats up everything at the end
@@ -531,9 +532,7 b' def make_map(config):'
531 532 action='my_account_update', conditions={'method': ['POST']})
532 533
533 534 m.connect('my_account_password', '/my_account/password',
534 action='my_account_password', conditions={'method': ['GET']})
535 m.connect('my_account_password', '/my_account/password',
536 action='my_account_password_update', conditions={'method': ['POST']})
535 action='my_account_password', conditions={'method': ['GET', 'POST']})
537 536
538 537 m.connect('my_account_repos', '/my_account/repos',
539 538 action='my_account_repos', conditions={'method': ['GET']})
@@ -32,17 +32,21 b' from pylons.controllers.util import redi'
32 32 from pylons.i18n.translation import _
33 33 from sqlalchemy.orm import joinedload
34 34
35 from rhodecode import forms
35 36 from rhodecode.lib import helpers as h
36 37 from rhodecode.lib import auth
37 38 from rhodecode.lib.auth import (
38 39 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
39 40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import jsonify
40 42 from rhodecode.lib.utils2 import safe_int, md5
41 43 from rhodecode.lib.ext_json import json
44
45 from rhodecode.model.validation_schema.schemas import user_schema
42 46 from rhodecode.model.db import (
43 47 Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
44 48 UserFollowing)
45 from rhodecode.model.forms import UserForm, PasswordChangeForm
49 from rhodecode.model.forms import UserForm
46 50 from rhodecode.model.scm import RepoList
47 51 from rhodecode.model.user import UserModel
48 52 from rhodecode.model.repo import RepoModel
@@ -185,38 +189,44 b' class MyAccountController(BaseController'
185 189 force_defaults=False
186 190 )
187 191
188 @auth.CSRFRequired()
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
192 @auth.CSRFRequired(except_methods=['GET'])
217 193 def my_account_password(self):
218 194 c.active = 'password'
219 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 230 return render('admin/my_account/my_account.html')
221 231
222 232 def my_account_repos(self):
@@ -352,11 +362,10 b' class MyAccountController(BaseController'
352 362 return render('admin/my_account/my_account.html')
353 363
354 364 @auth.CSRFRequired()
365 @jsonify
355 366 def my_notifications_toggle_visibility(self):
356 367 user = c.rhodecode_user.get_instance()
357 user_data = user.user_data
358 status = user_data.get('notification_status', False)
359 user_data['notification_status'] = not status
360 user.user_data = user_data
368 new_status = not user.user_data.get('notification_status', True)
369 user.update_userdata(notification_status=new_status)
361 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 135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136 136
137 137 application_form = ApplicationUiSettingsForm()()
138
138 139 try:
139 140 form_result = application_form.to_python(dict(request.POST))
140 141 except formencode.Invalid as errors:
@@ -151,12 +152,14 b' class SettingsController(BaseController)'
151 152 )
152 153
153 154 try:
154 model.update_global_ssl_setting(form_result['web_push_ssl'])
155 155 if c.visual.allow_repo_location_change:
156 156 model.update_global_path_setting(
157 157 form_result['paths_root_path'])
158
159 model.update_global_ssl_setting(form_result['web_push_ssl'])
158 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 163 model.create_or_update_global_hg_settings(form_result)
161 164 model.create_or_update_global_pr_settings(form_result)
162 165 except Exception:
@@ -789,18 +792,5 b' LabSetting = collections.namedtuple('
789 792 # This list has to be kept in sync with the form
790 793 # rhodecode.model.forms.LabsSettingsForm.
791 794 _LAB_SETTINGS = [
792 LabSetting(
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 ),
795
806 796 ]
@@ -44,6 +44,8 b' from rhodecode.lib.vcs.backends.base imp'
44 44 from rhodecode.lib.vcs.exceptions import (
45 45 CommitError, EmptyRepositoryError, NodeDoesNotExistError)
46 46 from rhodecode.model.db import Statistics, CacheKey, User
47 from rhodecode.model.repo import ReadmeFinder
48
47 49
48 50 log = logging.getLogger(__name__)
49 51
@@ -61,37 +63,16 b' class SummaryController(BaseRepoControll'
61 63 @cache_region('long_term')
62 64 def _generate_readme(cache_key):
63 65 readme_data = None
64 readme_file = None
65 try:
66 # gets the landing revision or tip if fails
67 commit = db_repo.get_landing_commit()
68 if isinstance(commit, EmptyCommit):
69 raise EmptyRepositoryError()
70 renderer = MarkupRenderer()
71 for f in renderer.pick_readme_order(default_renderer):
72 try:
73 node = commit.get_node(f)
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
66 readme_node = None
67 readme_filename = None
68 commit = self._get_landing_commit_or_none(db_repo)
69 if commit:
70 log.debug("Searching for a README file.")
71 readme_node = ReadmeFinder(default_renderer).search(commit)
72 if readme_node:
73 readme_data = self._render_readme_or_none(commit, readme_node)
74 readme_filename = readme_node.path
75 return readme_data, readme_filename
95 76
96 77 invalidator_context = CacheKey.repo_context_cache(
97 78 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
@@ -102,11 +83,36 b' class SummaryController(BaseRepoControll'
102 83
103 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 109 @LoginRequired()
107 110 @HasRepoPermissionAnyDecorator(
108 111 'repository.read', 'repository.write', 'repository.admin')
109 112 def index(self, repo_name):
113
114 # Prepare the clone URL
115
110 116 username = ''
111 117 if c.rhodecode_user.username != User.DEFAULT_USER:
112 118 username = safe_str(c.rhodecode_user.username)
@@ -124,6 +130,8 b' class SummaryController(BaseRepoControll'
124 130 c.clone_repo_url_id = c.rhodecode_db_repo.clone_url(
125 131 user=username, uri_tmpl=_def_clone_uri_by_id)
126 132
133 # If enabled, get statistics data
134
127 135 c.show_stats = bool(c.rhodecode_db_repo.enable_statistics)
128 136
129 137 stats = self.sa.query(Statistics)\
@@ -47,7 +47,7 b' def _commits_as_dict(commit_ids, repos):'
47 47 if not commit_ids:
48 48 return []
49 49
50 needed_commits = set(commit_ids)
50 needed_commits = list(commit_ids)
51 51
52 52 commits = []
53 53 reviewers = []
@@ -57,6 +57,7 b' def _commits_as_dict(commit_ids, repos):'
57 57
58 58 vcs_repo = repo.scm_instance(cache=False)
59 59 try:
60 # use copy of needed_commits since we modify it while iterating
60 61 for commit_id in list(needed_commits):
61 62 try:
62 63 cs = vcs_repo.get_changeset(commit_id)
@@ -78,7 +79,7 b' def _commits_as_dict(commit_ids, repos):'
78 79 repo.repo_name)
79 80 commits.append(cs_data)
80 81
81 needed_commits.discard(commit_id)
82 needed_commits.remove(commit_id)
82 83
83 84 except Exception as e:
84 85 log.exception(e)
@@ -1,21 +1,22 b''
1 # English translations for rhodecode.
2 # Copyright (C) 2015 RhodeCode GmbH
3 # This file is distributed under the same license as the rhodecode project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
1 # Translations template for rhodecode-enterprise-ce.
2 # Copyright (C) 2016 RhodeCode GmbH
3 # This file is distributed under the same license as the rhodecode-enterprise-ce project.
5 4 #
5 # Translators:
6 6 msgid ""
7 7 msgstr ""
8 "Project-Id-Version: rhodecode 0.1\n"
9 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
8 "Project-Id-Version: RhodeCode\n"
9 "Report-Msgid-Bugs-To: marcin@rhodecode.com\n"
10 10 "POT-Creation-Date: 2013-06-01 18:38+0200\n"
11 11 "PO-Revision-Date: 2011-02-25 19:13+0100\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: en <LL@li.org>\n"
14 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
12 "Last-Translator: Marcin Kuzminski <marcin@rhodecode.com>\n"
13 "Language-Team: en <admin@rhodecode.com>\n"
15 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 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 21 #: rhodecode/controllers/changelog.py:149
21 22 msgid "All Branches"
@@ -20,7 +20,7 b''
20 20
21 21 import logging
22 22
23 from rhodecode.model.db import Repository, Integration
23 from rhodecode.model.db import Repository, Integration, RepoGroup
24 24 from rhodecode.config.routing import (
25 25 ADMIN_PREFIX, add_route_requirements, URL_NAME_REQUIREMENTS)
26 26 from rhodecode.integrations import integration_type_registry
@@ -29,6 +29,17 b' log = logging.getLogger(__name__)'
29 29
30 30
31 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 43 config.add_route('global_integrations_home',
33 44 ADMIN_PREFIX + '/integrations')
34 45 config.add_route('global_integrations_list',
@@ -46,18 +57,80 b' def includeme(config):'
46 57 config.add_route('global_integrations_edit',
47 58 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
48 59 custom_predicates=(valid_integration,))
60
61
49 62 for route_name in ['global_integrations_create', 'global_integrations_edit']:
50 63 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
51 64 attr='settings_get',
52 renderer='rhodecode:templates/admin/integrations/edit.html',
65 renderer='rhodecode:templates/admin/integrations/form.html',
53 66 request_method='GET',
54 67 route_name=route_name)
55 68 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
56 69 attr='settings_post',
57 renderer='rhodecode:templates/admin/integrations/edit.html',
70 renderer='rhodecode:templates/admin/integrations/form.html',
58 71 request_method='POST',
59 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 134 config.add_route('repo_integrations_home',
62 135 add_route_requirements(
63 136 '{repo_name}/settings/integrations',
@@ -74,8 +147,21 b' def includeme(config):'
74 147 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
75 148 attr='index',
76 149 request_method='GET',
150 renderer='rhodecode:templates/admin/integrations/list.html',
77 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 165 config.add_route('repo_integrations_create',
80 166 add_route_requirements(
81 167 '{repo_name}/settings/integrations/{integration}/new',
@@ -91,12 +177,12 b' def includeme(config):'
91 177 for route_name in ['repo_integrations_edit', 'repo_integrations_create']:
92 178 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
93 179 attr='settings_get',
94 renderer='rhodecode:templates/admin/integrations/edit.html',
180 renderer='rhodecode:templates/admin/integrations/form.html',
95 181 request_method='GET',
96 182 route_name=route_name)
97 183 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
98 184 attr='settings_post',
99 renderer='rhodecode:templates/admin/integrations/edit.html',
185 renderer='rhodecode:templates/admin/integrations/form.html',
100 186 request_method='POST',
101 187 route_name=route_name)
102 188
@@ -107,20 +193,37 b' def valid_repo(info, request):'
107 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 203 def valid_integration(info, request):
111 204 integration_type = info['match']['integration']
112 205 integration_id = info['match'].get('integration_id')
113 206 repo_name = info['match'].get('repo_name')
207 repo_group_name = info['match'].get('repo_group_name')
114 208
115 209 if integration_type not in integration_type_registry:
116 210 return False
117 211
118 repo = None
212 repo, repo_group = None, None
119 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 215 if not repo:
122 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 227 if integration_id:
125 228 integration = Integration.get(integration_id)
126 229 if not integration:
@@ -129,5 +232,7 b' def valid_integration(info, request):'
129 232 return False
130 233 if repo and repo.repo_id != integration.repo_id:
131 234 return False
235 if repo_group and repo_group.group_id != integration.repo_group_id:
236 return False
132 237
133 238 return True
@@ -20,26 +20,52 b''
20 20
21 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):
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 """
26 class IntegrationOptionsSchemaBase(colander.MappingSchema):
32 27 enabled = colander.SchemaNode(
33 28 colander.Bool(),
34 29 default=True,
35 description=lazy_ugettext('Enable or disable this integration.'),
30 description=_('Enable or disable this integration.'),
36 31 missing=False,
37 title=lazy_ugettext('Enabled'),
32 title=_('Enabled'),
38 33 )
39 34
40 35 name = colander.SchemaNode(
41 36 colander.String(),
42 description=lazy_ugettext('Short name for this integration.'),
37 description=_('Short name for this integration.'),
43 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 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 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 25 class IntegrationTypeBase(object):
25 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 91 def __init__(self, settings):
28 92 """
29 93 :param settings: dict of settings to be used for the integration
30 94 """
31 95 self.settings = settings
32 96
33
34 97 def settings_schema(self):
35 98 """
36 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()
42
101 return colander.Schema()
@@ -26,11 +26,10 b' import colander'
26 26 from mako.template import Template
27 27
28 28 from rhodecode import events
29 from rhodecode.translation import _, lazy_ugettext
29 from rhodecode.translation import _
30 30 from rhodecode.lib.celerylib import run_task
31 31 from rhodecode.lib.celerylib import tasks
32 32 from rhodecode.integrations.types.base import IntegrationTypeBase
33 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
34 33
35 34
36 35 log = logging.getLogger(__name__)
@@ -147,18 +146,79 b" repo_push_template_html = Template('''"
147 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 212 @colander.instantiate(validator=colander.Length(min=1))
153 213 class recipients(colander.SequenceSchema):
154 title = lazy_ugettext('Recipients')
155 description = lazy_ugettext('Email addresses to send push events to')
214 title = _('Recipients')
215 description = _('Email addresses to send push events to')
156 216 widget = deform.widget.SequenceWidget(min_len=1)
157 217
158 218 recipient = colander.SchemaNode(
159 219 colander.String(),
160 title=lazy_ugettext('Email address'),
161 description=lazy_ugettext('Email address'),
220 title=_('Email address'),
221 description=_('Email address'),
162 222 default='',
163 223 validator=colander.Email(),
164 224 widget=deform.widget.TextInputWidget(
@@ -169,8 +229,9 b' class EmailSettingsSchema(IntegrationSet'
169 229
170 230 class EmailIntegrationType(IntegrationTypeBase):
171 231 key = 'email'
172 display_name = lazy_ugettext('Email')
173 SettingsSchema = EmailSettingsSchema
232 display_name = _('Email')
233 description = _('Send repo push summaries to a list of recipients via email')
234 icon = email_icon
174 235
175 236 def settings_schema(self):
176 237 schema = EmailSettingsSchema()
@@ -29,29 +29,28 b' from celery.task import task'
29 29 from mako.template import Template
30 30
31 31 from rhodecode import events
32 from rhodecode.translation import lazy_ugettext
32 from rhodecode.translation import _
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib.celerylib import run_task
35 35 from rhodecode.lib.colander_utils import strip_whitespace
36 36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38 37
39 38 log = logging.getLogger(__name__)
40 39
41 40
42 class HipchatSettingsSchema(IntegrationSettingsSchemaBase):
41 class HipchatSettingsSchema(colander.Schema):
43 42 color_choices = [
44 ('yellow', lazy_ugettext('Yellow')),
45 ('red', lazy_ugettext('Red')),
46 ('green', lazy_ugettext('Green')),
47 ('purple', lazy_ugettext('Purple')),
48 ('gray', lazy_ugettext('Gray')),
43 ('yellow', _('Yellow')),
44 ('red', _('Red')),
45 ('green', _('Green')),
46 ('purple', _('Purple')),
47 ('gray', _('Gray')),
49 48 ]
50 49
51 50 server_url = colander.SchemaNode(
52 51 colander.String(),
53 title=lazy_ugettext('Hipchat server URL'),
54 description=lazy_ugettext('Hipchat integration url.'),
52 title=_('Hipchat server URL'),
53 description=_('Hipchat integration url.'),
55 54 default='',
56 55 preparer=strip_whitespace,
57 56 validator=colander.url,
@@ -61,15 +60,15 b' class HipchatSettingsSchema(IntegrationS'
61 60 )
62 61 notify = colander.SchemaNode(
63 62 colander.Bool(),
64 title=lazy_ugettext('Notify'),
65 description=lazy_ugettext('Make a notification to the users in room.'),
63 title=_('Notify'),
64 description=_('Make a notification to the users in room.'),
66 65 missing=False,
67 66 default=False,
68 67 )
69 68 color = colander.SchemaNode(
70 69 colander.String(),
71 title=lazy_ugettext('Color'),
72 description=lazy_ugettext('Background color of message.'),
70 title=_('Color'),
71 description=_('Background color of message.'),
73 72 missing='',
74 73 validator=colander.OneOf([x[0] for x in color_choices]),
75 74 widget=deform.widget.Select2Widget(
@@ -79,29 +78,28 b' class HipchatSettingsSchema(IntegrationS'
79 78
80 79
81 80 repo_push_template = Template('''
82 <b>${data['actor']['username']}</b> pushed to
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>
81 <b>${data['actor']['username']}</b> pushed to repo <a href="${data['repo']['url']}">${data['repo']['repo_name']}</a>:
90 82 <br>
91 83 <ul>
92 %for commit in data['push']['commits']:
84 %for branch, branch_commits in branches_commits.items():
93 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 92 </li>
96 93 %endfor
97 </ul>
98 94 ''')
99 95
100 96
101
102 97 class HipchatIntegrationType(IntegrationTypeBase):
103 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 103 valid_events = [
106 104 events.PullRequestCloseEvent,
107 105 events.PullRequestMergeEvent,
@@ -217,8 +215,23 b' class HipchatIntegrationType(Integration'
217 215 )
218 216
219 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 232 result = repo_push_template.render(
221 233 data=data,
234 branches_commits=branches_commits,
222 235 )
223 236 return result
224 237
@@ -29,21 +29,20 b' from celery.task import task'
29 29 from mako.template import Template
30 30
31 31 from rhodecode import events
32 from rhodecode.translation import lazy_ugettext
32 from rhodecode.translation import _
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib.celerylib import run_task
35 35 from rhodecode.lib.colander_utils import strip_whitespace
36 36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38 37
39 38 log = logging.getLogger(__name__)
40 39
41 40
42 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
41 class SlackSettingsSchema(colander.Schema):
43 42 service = colander.SchemaNode(
44 43 colander.String(),
45 title=lazy_ugettext('Slack service URL'),
46 description=h.literal(lazy_ugettext(
44 title=_('Slack service URL'),
45 description=h.literal(_(
47 46 'This can be setup at the '
48 47 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
49 48 'slack app manager</a>')),
@@ -56,8 +55,8 b' class SlackSettingsSchema(IntegrationSet'
56 55 )
57 56 username = colander.SchemaNode(
58 57 colander.String(),
59 title=lazy_ugettext('Username'),
60 description=lazy_ugettext('Username to show notifications coming from.'),
58 title=_('Username'),
59 description=_('Username to show notifications coming from.'),
61 60 missing='Rhodecode',
62 61 preparer=strip_whitespace,
63 62 widget=deform.widget.TextInputWidget(
@@ -66,8 +65,8 b' class SlackSettingsSchema(IntegrationSet'
66 65 )
67 66 channel = colander.SchemaNode(
68 67 colander.String(),
69 title=lazy_ugettext('Channel'),
70 description=lazy_ugettext('Channel to send notifications to.'),
68 title=_('Channel'),
69 description=_('Channel to send notifications to.'),
71 70 missing='',
72 71 preparer=strip_whitespace,
73 72 widget=deform.widget.TextInputWidget(
@@ -76,8 +75,8 b' class SlackSettingsSchema(IntegrationSet'
76 75 )
77 76 icon_emoji = colander.SchemaNode(
78 77 colander.String(),
79 title=lazy_ugettext('Emoji'),
80 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
78 title=_('Emoji'),
79 description=_('Emoji to use eg. :studio_microphone:'),
81 80 missing='',
82 81 preparer=strip_whitespace,
83 82 widget=deform.widget.TextInputWidget(
@@ -87,25 +86,22 b' class SlackSettingsSchema(IntegrationSet'
87 86
88 87
89 88 repo_push_template = Template(r'''
90 *${data['actor']['username']}* pushed to \
91 %if data['push']['branches']:
92 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
93 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
94 %else:
95 unknown branch \
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}
89 *${data['actor']['username']}* pushed to repo <${data['repo']['url']}|${data['repo']['repo_name']}>:
90 %for branch, branch_commits in branches_commits.items():
91 branch: <${branch_commits['branch']['url']}|${branch_commits['branch']['name']}>
92 %for commit in branch_commits['commits']:
93 > <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
94 %endfor
101 95 %endfor
102 96 ''')
103 97
104 98
105 99 class SlackIntegrationType(IntegrationTypeBase):
106 100 key = 'slack'
107 display_name = lazy_ugettext('Slack')
108 SettingsSchema = SlackSettingsSchema
101 display_name = _('Slack')
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 105 valid_events = [
110 106 events.PullRequestCloseEvent,
111 107 events.PullRequestMergeEvent,
@@ -221,8 +217,23 b' class SlackIntegrationType(IntegrationTy'
221 217 )
222 218
223 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 234 result = repo_push_template.render(
225 235 data=data,
236 branches_commits=branches_commits,
226 237 html_to_slack_links=html_to_slack_links,
227 238 )
228 239 return result
@@ -28,19 +28,19 b' from celery.task import task'
28 28 from mako.template import Template
29 29
30 30 from rhodecode import events
31 from rhodecode.translation import lazy_ugettext
31 from rhodecode.translation import _
32 32 from rhodecode.integrations.types.base import IntegrationTypeBase
33 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
34 33
35 34 log = logging.getLogger(__name__)
36 35
37 36
38 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
37 class WebhookSettingsSchema(colander.Schema):
39 38 url = colander.SchemaNode(
40 39 colander.String(),
41 title=lazy_ugettext('Webhook URL'),
42 description=lazy_ugettext('URL of the webhook to receive POST event.'),
43 default='',
40 title=_('Webhook URL'),
41 description=_('URL of the webhook to receive POST event.'),
42 missing=colander.required,
43 required=True,
44 44 validator=colander.url,
45 45 widget=deform.widget.TextInputWidget(
46 46 placeholder='https://www.example.com/webhook'
@@ -48,18 +48,24 b' class WebhookSettingsSchema(IntegrationS'
48 48 )
49 49 secret_token = colander.SchemaNode(
50 50 colander.String(),
51 title=lazy_ugettext('Secret Token'),
52 description=lazy_ugettext('String used to validate received payloads.'),
51 title=_('Secret Token'),
52 description=_('String used to validate received payloads.'),
53 53 default='',
54 missing='',
54 55 widget=deform.widget.TextInputWidget(
55 56 placeholder='secret_token'
56 57 ),
57 58 )
58 59
59 60
61
62
60 63 class WebhookIntegrationType(IntegrationTypeBase):
61 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 69 valid_events = [
64 70 events.PullRequestCloseEvent,
65 71 events.PullRequestMergeEvent,
@@ -18,23 +18,29 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 import colander
22 import logging
23 21 import pylons
24 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 29 from pyramid.renderers import render
28 30 from pyramid.response import Response
29 31
30 32 from rhodecode.lib import auth
31 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 37 from rhodecode.model.scm import ScmModel
34 38 from rhodecode.model.integration import IntegrationModel
35 39 from rhodecode.admin.navigation import navigation_list
36 40 from rhodecode.translation import _
37 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 45 log = logging.getLogger(__name__)
40 46
@@ -59,28 +65,51 b' class IntegrationSettingsViewBase(object'
59 65
60 66 self.IntegrationType = None
61 67 self.repo = None
68 self.repo_group = None
62 69 self.integration = None
63 70 self.integrations = {}
64 71
65 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 75 repo_name = request.matchdict['repo_name']
69 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 84 integration_type = request.matchdict['integration']
73 85 self.IntegrationType = integration_type_registry[integration_type]
74 86
75 87 if 'integration_id' in request.matchdict: # single integration context
76 88 integration_id = request.matchdict['integration_id']
77 89 self.integration = Integration.get(integration_id)
78 else: # list integrations context
79 for integration in IntegrationModel().get_integrations(self.repo):
80 self.integrations.setdefault(integration.integration_type, []
81 ).append(integration)
90
91 # extra perms check just in case
92 if not self._has_perms_for_integration(self.integration):
93 raise HTTPForbidden()
82 94
83 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 114 def _template_c_context(self):
86 115 # TODO: dan: this is a stopgap in order to inherit from current pylons
@@ -91,7 +120,10 b' class IntegrationSettingsViewBase(object'
91 120 c.active = 'integrations'
92 121 c.rhodecode_user = self.request.user
93 122 c.repo = self.repo
123 c.repo_group = self.repo_group
94 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 127 if self.repo:
96 128 c.repo_info = self.repo
97 129 c.rhodecode_db_repo = self.repo
@@ -102,34 +134,77 b' class IntegrationSettingsViewBase(object'
102 134 return c
103 135
104 136 def _form_schema(self):
105 if self.integration:
106 settings = self.integration.settings
107 else:
108 settings = {}
109 return self.IntegrationType(settings=settings).settings_schema()
137 schema = make_integration_schema(IntegrationType=self.IntegrationType,
138 settings=self.settings)
110 139
111 def settings_get(self, defaults=None, errors=None, form=None):
112 """
113 View that displays the plugin settings as a form.
114 """
115 defaults = defaults or {}
116 errors = errors or {}
140 # returns a clone, important if mutating the schema later
141 return schema.bind(
142 permissions=self.request.user.permissions,
143 no_scope=not self.admin_view)
144
145
146 def _form_defaults(self):
147 defaults = {}
117 148
118 149 if self.integration:
119 defaults = self.integration.settings or {}
120 defaults['name'] = self.integration.name
121 defaults['enabled'] = self.integration.enabled
150 defaults['settings'] = self.integration.settings or {}
151 defaults['options'] = {
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 160 else:
123 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 167 else:
126 168 scope = _('Global')
127 169
128 defaults['name'] = '{} {} integration'.format(scope,
129 self.IntegrationType.display_name)
130 defaults['enabled'] = True
170 defaults['options'] = {
171 '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 209 if self.integration:
135 210 buttons = ('submit', 'delete')
@@ -138,23 +213,10 b' class IntegrationSettingsViewBase(object'
138 213
139 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 216 template_context = {
150 217 'form': form,
151 'defaults': defaults,
152 'errors': errors,
153 'schema': schema,
154 218 'current_IntegrationType': self.IntegrationType,
155 219 'integration': self.integration,
156 'settings': self.settings,
157 'resource': self.context,
158 220 'c': self._template_c_context(),
159 221 }
160 222
@@ -163,71 +225,93 b' class IntegrationSettingsViewBase(object'
163 225 @auth.CSRFRequired()
164 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'):
169 Session().delete(self.integration)
170 Session().commit()
171 self.request.session.flash(
172 _('Integration {integration_name} deleted successfully.').format(
173 integration_name=self.integration.name),
174 queue='success')
175 if self.repo:
176 redirect_to = self.request.route_url(
177 'repo_integrations_home', repo_name=self.repo.repo_name)
178 else:
179 redirect_to = self.request.route_url('global_integrations_home')
180 raise HTTPFound(redirect_to)
230 controls = self.request.POST.items()
231 pstruct = peppercorn.parse(controls)
232
233 if self.integration and pstruct.get('delete'):
234 return self._delete_integration(self.integration)
235
236 schema = self._form_schema()
237
238 skip_settings_validation = False
239 if self.integration and 'enabled' not in pstruct.get('options', {}):
240 skip_settings_validation = True
241 schema['settings'].validator = None
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 = {}
187 for node in schema.children:
188 if type(node.typ) in (colander.Set, colander.List):
189 val = self.request.params.getall(node.name)
190 else:
191 val = self.request.params.get(node.name)
192 if val:
193 params[node.name] = val
253 if not self.admin_view:
254 # scope is read only field in these cases, and has to be added
255 options = pstruct.setdefault('options', {})
256 if 'scope' not in options:
257 options['scope'] = IntegrationScopeType().serialize(None, {
258 'repo': self.repo,
259 'repo_group': self.repo_group,
260 })
194 261
195 controls = self.request.POST.items()
196 262 try:
197 valid_data = form.validate(controls)
263 valid_data = form.validate_pstruct(pstruct)
198 264 except deform.ValidationFailure as e:
199 265 self.request.session.flash(
200 266 _('Errors exist when saving integration settings. '
201 267 'Please check the form inputs.'),
202 268 queue='error')
203 return self.settings_get(errors={}, defaults=params, form=e)
269 return self.settings_get(form=e)
204 270
205 271 if not self.integration:
206 272 self.integration = Integration()
207 273 self.integration.integration_type = self.IntegrationType.key
208 if self.repo:
209 self.integration.repo = self.repo
210 274 Session().add(self.integration)
211 275
212 self.integration.enabled = valid_data.pop('enabled', False)
213 self.integration.name = valid_data.pop('name')
214 self.integration.settings = valid_data
276 scope = valid_data['options']['scope']
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 289 Session().commit()
217
218 290 # Display success message and redirect.
219 291 self.request.session.flash(
220 292 _('Integration {integration_name} updated successfully.').format(
221 293 integration_name=self.IntegrationType.display_name),
222 294 queue='success')
223 295
224 if self.repo:
225 redirect_to = self.request.route_url(
226 'repo_integrations_edit', repo_name=self.repo.repo_name,
296
297 # if integration scope changes, we must redirect to the right place
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 311 integration=self.integration.integration_type,
228 312 integration_id=self.integration.integration_id)
229 313 else:
230 redirect_to = self.request.route_url(
314 redirect_to = self.request.route_path(
231 315 'global_integrations_edit',
232 316 integration=self.integration.integration_type,
233 317 integration_id=self.integration.integration_id)
@@ -235,31 +319,60 b' class IntegrationSettingsViewBase(object'
235 319 return HTTPFound(redirect_to)
236 320
237 321 def index(self):
238 current_integrations = self.integrations
239 if self.IntegrationType:
240 current_integrations = {
241 self.IntegrationType.key: self.integrations.get(
242 self.IntegrationType.key, [])
243 }
322 """ List integrations """
323 if self.repo:
324 scope = self.repo
325 elif self.repo_group:
326 scope = self.repo_group
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 359 template_context = {
360 'sort_field': sort_field,
361 'rev_sort_dir': sort_dir != 'desc' and 'desc' or 'asc',
246 362 'current_IntegrationType': self.IntegrationType,
247 'current_integrations': current_integrations,
363 'integrations_list': integrations,
248 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:
253 html = render('rhodecode:templates/admin/integrations/list.html',
254 template_context,
255 request=self.request)
256 else:
257 html = render('rhodecode:templates/admin/integrations/list.html',
258 template_context,
259 request=self.request)
260
261 return Response(html)
262
370 def new_integration(self):
371 template_context = {
372 'available_integrations': integration_type_registry,
373 'c': self._template_c_context(),
374 }
375 return template_context
263 376
264 377 class GlobalIntegrationsView(IntegrationSettingsViewBase):
265 378 def perm_check(self, user):
@@ -270,3 +383,10 b' class RepoIntegrationsView(IntegrationSe'
270 383 def perm_check(self, user):
271 384 return auth.HasRepoPermissionAll('repository.admin'
272 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 48 :param headers: dictionary with headers (keys are whats in ``order``
49 49 parameter)
50 50 """
51 from rhodecode.lib.utils import get_custom_lexer
51 from rhodecode.lib.helpers import get_lexer_for_filenode
52 52 options['linenos'] = True
53 53 formatter = AnnotateHtmlFormatter(
54 54 filenode=filenode, order=order, headers=headers,
55 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 57 highlighted = highlight(filenode.content, lexer, formatter)
58 58 return highlighted
59 59
@@ -1116,9 +1116,11 b' class CSRFRequired(object):'
1116 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 1121 self.token = token
1121 1122 self.header = header
1123 self.except_methods = except_methods or []
1122 1124
1123 1125 def __call__(self, func):
1124 1126 return get_cython_compat_decorator(self.__wrapper, func)
@@ -1131,6 +1133,9 b' class CSRFRequired(object):'
1131 1133 return supplied_token and supplied_token == cur_token
1132 1134
1133 1135 def __wrapper(self, func, *fargs, **fkwargs):
1136 if request.method in self.except_methods:
1137 return func(*fargs, **fkwargs)
1138
1134 1139 cur_token = get_csrf_token(save_if_missing=False)
1135 1140 if self.check_csrf(request, cur_token):
1136 1141 if request.POST.get(self.token):
@@ -28,6 +28,7 b' import logging'
28 28 import socket
29 29
30 30 import ipaddress
31 import pyramid.threadlocal
31 32
32 33 from paste.auth.basic import AuthBasicAuthenticator
33 34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
@@ -276,7 +277,7 b' def attach_context_attributes(context, r'
276 277 # Visual options
277 278 context.visual = AttributeDict({})
278 279
279 # DB store
280 # DB stored Visual Items
280 281 context.visual.show_public_icon = str2bool(
281 282 rc_config.get('rhodecode_show_public_icon'))
282 283 context.visual.show_private_icon = str2bool(
@@ -368,6 +369,8 b' def attach_context_attributes(context, r'
368 369 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(
369 370 context.rhodecode_user.user_id)
370 371
372 context.pyramid_request = pyramid.threadlocal.get_current_request()
373
371 374
372 375 def get_auth_user(environ):
373 376 ip_addr = get_ip_addr(environ)
@@ -84,6 +84,7 b' def get_user_data(user_id):'
84 84 'icon_link': h.gravatar_url(user.email, 14),
85 85 'display_name': h.person(user, 'username_or_name_or_email'),
86 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
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 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
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
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