Commit ea82bcd396c9e6ab8542474ee70365297a4ff2dc
Exists in
spb-stable
and in
3 other branches
Merge branch 'master' into request/relative_submodules
Conflicts: CHANGELOG
Showing
457 changed files
with
8091 additions
and
6375 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 457 files displayed.
.gitignore
.rspec
CHANGELOG
1 | +v 6.8.0 | |
2 | + - Ability to at mention users that are participating in issue and merge req. discussion | |
3 | + - Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu) | |
4 | + - Add support for relative submodules | |
5 | + | |
6 | +v 6.7.2 | |
7 | + - Fix upgrader script | |
8 | + | |
9 | +v 6.7.1 | |
10 | + - Fix GitLab CI integration | |
11 | + | |
1 | 12 | v 6.7.0 |
13 | + - Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations | |
2 | 14 | - Add support for Gemnasium as a Project Service (Olivier Gonzalez) |
3 | 15 | - Add edit file button to MergeRequest diff |
4 | 16 | - Public groups (Jason Hollingsworth) |
5 | 17 | - Cleaner headers in Notification Emails (Pierre de La Morinerie) |
6 | - - Add support for relative submodules | |
18 | + - Blob and tree gfm links to anchors work | |
19 | + - Piwik Integration (Sebastian Winkler) | |
20 | + - Show contribution guide link for new issue form (Jeroen van Baarsen) | |
21 | + - Fix CI status for merge requests from fork | |
22 | + - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) | |
23 | + - New page load indicator that includes a spinner that scrolls with the page | |
24 | + - Converted all the help sections into markdown | |
25 | + - LDAP user filters | |
26 | + - Streamline the content of notification emails (Pierre de La Morinerie) | |
27 | + - Fixes a bug with group member administration (Matt DeTullio) | |
28 | + - Sort tag names using VersionSorter (Robert Speicher) | |
29 | + - Add GFM autocompletion for MergeRequests (Robert Speicher) | |
30 | + - Add webhook when a new tag is pushed (Jeroen van Baarsen) | |
31 | + - Add button for toggling inline comments in diff view | |
32 | + - Add retry feature for repository import | |
33 | + - Reuse the GitLab LDAP connection within each request | |
34 | + - Changed markdown new line behaviour to conform to markdown standards | |
35 | + - Fix global search | |
36 | + - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5) | |
37 | + - Create and Update MR calls now support the description parameter (Greg Messner) | |
38 | + - Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository | |
39 | + - Added Slack service integration (Federico Ravasio) | |
40 | + - Better API responses for access_levels (sponsored by O'Reilly Media) | |
41 | + - Requires at least 2 unicorn workers | |
42 | + - Requires gitlab-shell v1.9+ | |
43 | + - Replaced gemoji(due to closed licencing problem) with Phantom Open Emoji library(combined SIL Open Font License, MIT License and the CC 3.0 License) | |
44 | + - Fix `/:username.keys` response content type (Dmitry Medvinsky) | |
45 | + | |
46 | +v 6.6.5 | |
47 | + - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) | |
48 | + - Hide mr close button for comment form if merge request was closed or inline comment | |
49 | + - Adds ability to reopen closed merge request | |
50 | + | |
51 | +v 6.6.4 | |
52 | + - Add missing html escape for highlighted code blocks in comments, issues | |
53 | + | |
54 | +v 6.6.3 | |
55 | + - Fix 500 error when edit yourself from admin area | |
56 | + - Hide private groups for public profiles | |
7 | 57 | |
8 | 58 | v 6.6.2 |
9 | 59 | - Fix 500 error on branch/tag create or remove via UI |
... | ... | @@ -25,7 +75,7 @@ v 6.6.0 |
25 | 75 | - Remove snippet expiration |
26 | 76 | - Mobile UI improvements (Drew Blessing) |
27 | 77 | - Fix block/remove UI for admin::users#show page |
28 | - - Show users' group membership on users' activity page | |
78 | + - Show users' group membership on users' activity page (Robert Djurasaj) | |
29 | 79 | - User pages are visible without login if user is authorized to a public project |
30 | 80 | - Markdown rendered headers have id derived from their name and link to their id |
31 | 81 | - Improve application to work faster with large groups (100+ members) |
... | ... | @@ -34,7 +84,7 @@ v 6.6.0 |
34 | 84 | - Restyle Issue#show page and MR#show page |
35 | 85 | - Ability to filter by multiple labels for Issues page |
36 | 86 | - Rails version to 4.0.3 |
37 | - - Blob and tree gfm links to anchors work | |
87 | + - Fixed attachment identifier displaying underneath note text (Jason Blanchard) | |
38 | 88 | |
39 | 89 | v 6.5.1 |
40 | 90 | - Fix branch selectbox when create merge request from fork | ... | ... |
CONTRIBUTING.md
... | ... | @@ -22,11 +22,14 @@ Issues and merge requests should be in English and contain appropriate language |
22 | 22 | |
23 | 23 | ## Issue tracker |
24 | 24 | |
25 | -To get support for your particular problem please use the channels as detailed in the getting help section of [the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/). | |
25 | +To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/). | |
26 | 26 | |
27 | -The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. | |
27 | +The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md). | |
28 | +If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request. | |
29 | +When submitting an issue please conform to the issue submission guidelines listed below. | |
30 | +Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. | |
28 | 31 | |
29 | -Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose. | |
32 | +Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. | |
30 | 33 | |
31 | 34 | Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. |
32 | 35 | |
... | ... | @@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai |
48 | 51 | |
49 | 52 | ## Merge requests |
50 | 53 | |
51 | -We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI. | |
54 | +We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feature request forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI. | |
52 | 55 | |
53 | 56 | ### Merge request guidelines |
54 | 57 | |
... | ... | @@ -64,7 +67,7 @@ If you can, please submit a merge request with the fix or improvements including |
64 | 67 | 1. The MR title should describes the change you want to make |
65 | 68 | 1. The MR description should give a motive for your change and the method you used to achieve it |
66 | 69 | 1. If the MR changes the UI it should include before and after screenshots |
67 | -1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feedback items](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR | |
70 | +1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR | |
68 | 71 | 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion |
69 | 72 | 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). |
70 | 73 | |
... | ... | @@ -74,6 +77,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c |
74 | 77 | |
75 | 78 | For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). Please ensure that your merge request meets the following contribution acceptance criteria. |
76 | 79 | |
80 | +**Please format your merge request description as follows:** | |
81 | + | |
82 | +1. What does this MR do? | |
83 | +2. Are there points in the code the reviewer needs to double check? | |
84 | +3. Why was this MR needed? | |
85 | +4. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)? | |
86 | +5. Screenshots (If appropiate) | |
87 | + | |
77 | 88 | ## Contribution acceptance criteria |
78 | 89 | |
79 | 90 | 1. The change is as small as possible (see the above paragraph for details) | ... | ... |
Gemfile
... | ... | @@ -15,6 +15,9 @@ gem 'rails-observers' |
15 | 15 | gem 'actionpack-page_caching' |
16 | 16 | gem 'actionpack-action_caching' |
17 | 17 | |
18 | +# Default values for AR models | |
19 | +gem "default_value_for", "~> 3.0.0" | |
20 | + | |
18 | 21 | # Supported DBs |
19 | 22 | gem "mysql2", group: :mysql |
20 | 23 | gem "pg", group: :postgres |
... | ... | @@ -29,7 +32,7 @@ gem 'omniauth-github' |
29 | 32 | |
30 | 33 | # Extracting information from a git repository |
31 | 34 | # Provide access to Gitlab::Git library |
32 | -gem "gitlab_git", '~> 5.4.0' | |
35 | +gem "gitlab_git", '~> 5.7.1' | |
33 | 36 | |
34 | 37 | # Ruby/Rack Git Smart-HTTP Server Handler |
35 | 38 | gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' |
... | ... | @@ -45,7 +48,8 @@ gem "gitlab-linguist", "~> 3.0.0", require: "linguist" |
45 | 48 | |
46 | 49 | # API |
47 | 50 | gem "grape", "~> 0.6.1" |
48 | -gem "grape-entity", "~> 0.3.0" | |
51 | +# Replace with rubygems when nesteted entities get released | |
52 | +gem "grape-entity", "~> 0.4.1", ref: 'd904381c951e86250c3f44213b349a3dd8e83fb1', git: 'https://github.com/intridea/grape-entity.git' | |
49 | 53 | gem 'rack-cors', require: 'rack/cors' |
50 | 54 | |
51 | 55 | # Email validation |
... | ... | @@ -111,6 +115,7 @@ gem 'settingslogic' |
111 | 115 | |
112 | 116 | # Misc |
113 | 117 | gem "foreman" |
118 | +gem 'version_sorter' | |
114 | 119 | |
115 | 120 | # Cache |
116 | 121 | gem "redis-rails" |
... | ... | @@ -127,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2" |
127 | 132 | # Gemnasium integration |
128 | 133 | gem "gemnasium-gitlab-service", "~> 0.2" |
129 | 134 | |
135 | +# Slack integration | |
136 | +gem "slack-notifier", "~> 0.2.0" | |
137 | + | |
130 | 138 | # d3 |
131 | 139 | gem "d3_rails", "~> 3.1.4" |
132 | 140 | |
... | ... | @@ -157,14 +165,15 @@ gem "modernizr", "2.6.2" |
157 | 165 | gem "raphael-rails", "~> 2.1.2" |
158 | 166 | gem 'bootstrap-sass', '~> 3.0' |
159 | 167 | gem "font-awesome-rails", '~> 3.2' |
160 | -gem "gemoji", "~> 1.3.0" | |
168 | +gem "gitlab_emoji", "~> 0.0.1.1" | |
161 | 169 | gem "gon", '~> 5.0.0' |
170 | +gem 'nprogress-rails' | |
162 | 171 | |
163 | 172 | group :development do |
164 | 173 | gem "annotate", "~> 2.6.0.beta2" |
165 | 174 | gem "letter_opener" |
166 | 175 | gem 'quiet_assets', '~> 1.0.1' |
167 | - gem 'rack-mini-profiler' | |
176 | + gem 'rack-mini-profiler', require: false | |
168 | 177 | |
169 | 178 | # Better errors handler |
170 | 179 | gem 'better_errors' |
... | ... | @@ -209,7 +218,6 @@ group :development, :test do |
209 | 218 | # PhantomJS driver for Capybara |
210 | 219 | gem 'poltergeist', '~> 1.4.1' |
211 | 220 | |
212 | - gem 'spork', '~> 1.0rc' | |
213 | 221 | gem 'jasmine', '2.0.0.rc5' |
214 | 222 | |
215 | 223 | gem "spring", '1.1.1' | ... | ... |
Gemfile.lock
... | ... | @@ -5,6 +5,15 @@ GIT |
5 | 5 | specs: |
6 | 6 | github-markup (0.7.6) |
7 | 7 | |
8 | +GIT | |
9 | + remote: https://github.com/intridea/grape-entity.git | |
10 | + revision: d904381c951e86250c3f44213b349a3dd8e83fb1 | |
11 | + ref: d904381c951e86250c3f44213b349a3dd8e83fb1 | |
12 | + specs: | |
13 | + grape-entity (0.4.1) | |
14 | + activesupport | |
15 | + multi_json (>= 1.3.2) | |
16 | + | |
8 | 17 | GEM |
9 | 18 | remote: https://rubygems.org/ |
10 | 19 | specs: |
... | ... | @@ -101,6 +110,8 @@ GEM |
101 | 110 | daemons (1.1.9) |
102 | 111 | database_cleaner (1.2.0) |
103 | 112 | debug_inspector (0.0.2) |
113 | + default_value_for (3.0.0) | |
114 | + activerecord (>= 3.2.0, < 5.0) | |
104 | 115 | descendants_tracker (0.0.3) |
105 | 116 | devise (3.0.4) |
106 | 117 | bcrypt-ruby (~> 3.0) |
... | ... | @@ -117,6 +128,8 @@ GEM |
117 | 128 | mail (~> 2.2) |
118 | 129 | email_validator (1.4.0) |
119 | 130 | activemodel |
131 | + emoji (1.0.1) | |
132 | + json | |
120 | 133 | enumerize (0.7.0) |
121 | 134 | activesupport (>= 3.2) |
122 | 135 | equalizer (0.0.8) |
... | ... | @@ -154,7 +167,6 @@ GEM |
154 | 167 | formatador (0.2.4) |
155 | 168 | gemnasium-gitlab-service (0.2.1) |
156 | 169 | rugged (~> 0.19) |
157 | - gemoji (1.3.1) | |
158 | 170 | gherkin-ruby (0.3.1) |
159 | 171 | racc |
160 | 172 | github-markdown (0.5.5) |
... | ... | @@ -179,7 +191,9 @@ GEM |
179 | 191 | charlock_holmes (~> 0.6.6) |
180 | 192 | escape_utils (~> 0.2.4) |
181 | 193 | mime-types (~> 1.19) |
182 | - gitlab_git (5.4.0) | |
194 | + gitlab_emoji (0.0.1.1) | |
195 | + emoji (~> 1.0.1) | |
196 | + gitlab_git (5.7.1) | |
183 | 197 | activesupport (~> 4.0.0) |
184 | 198 | charlock_holmes (~> 0.6.9) |
185 | 199 | gitlab-grit (~> 2.6.1) |
... | ... | @@ -204,9 +218,6 @@ GEM |
204 | 218 | rack-accept |
205 | 219 | rack-mount |
206 | 220 | virtus (>= 1.0.0) |
207 | - grape-entity (0.3.0) | |
208 | - activesupport | |
209 | - multi_json (>= 1.3.2) | |
210 | 221 | growl (1.0.3) |
211 | 222 | guard (2.2.4) |
212 | 223 | formatador (>= 0.2.4) |
... | ... | @@ -233,7 +244,7 @@ GEM |
233 | 244 | httparty |
234 | 245 | httparty |
235 | 246 | http_parser.rb (0.5.3) |
236 | - httparty (0.12.0) | |
247 | + httparty (0.13.0) | |
237 | 248 | json (~> 1.8) |
238 | 249 | multi_xml (>= 0.5.2) |
239 | 250 | httpauth (0.2.0) |
... | ... | @@ -289,6 +300,7 @@ GEM |
289 | 300 | net-ssh (>= 1.99.1) |
290 | 301 | net-ssh (2.7.0) |
291 | 302 | nokogiri (1.5.10) |
303 | + nprogress-rails (0.1.2.3) | |
292 | 304 | oauth (0.4.7) |
293 | 305 | oauth2 (0.8.1) |
294 | 306 | faraday (~> 0.8) |
... | ... | @@ -460,6 +472,7 @@ GEM |
460 | 472 | rack-protection (~> 1.4) |
461 | 473 | tilt (~> 1.3, >= 1.3.4) |
462 | 474 | six (0.2.0) |
475 | + slack-notifier (0.2.0) | |
463 | 476 | slim (2.0.2) |
464 | 477 | temple (~> 0.6.6) |
465 | 478 | tilt (>= 1.3.3, < 2.1) |
... | ... | @@ -471,7 +484,6 @@ GEM |
471 | 484 | capybara (>= 2.0.0) |
472 | 485 | railties (>= 3) |
473 | 486 | spinach (>= 0.4) |
474 | - spork (1.0.0rc4) | |
475 | 487 | spring (1.1.1) |
476 | 488 | spring-commands-rspec (1.0.1) |
477 | 489 | spring (>= 0.9.1) |
... | ... | @@ -535,6 +547,7 @@ GEM |
535 | 547 | raindrops (~> 0.7) |
536 | 548 | unicorn-worker-killer (0.4.2) |
537 | 549 | unicorn (~> 4) |
550 | + version_sorter (1.1.0) | |
538 | 551 | virtus (1.0.1) |
539 | 552 | axiom-types (~> 0.0.5) |
540 | 553 | coercible (~> 1.0) |
... | ... | @@ -570,6 +583,7 @@ DEPENDENCIES |
570 | 583 | coveralls |
571 | 584 | d3_rails (~> 3.1.4) |
572 | 585 | database_cleaner |
586 | + default_value_for (~> 3.0.0) | |
573 | 587 | devise (= 3.0.4) |
574 | 588 | devise-async (= 0.8.0) |
575 | 589 | email_spec |
... | ... | @@ -581,18 +595,18 @@ DEPENDENCIES |
581 | 595 | font-awesome-rails (~> 3.2) |
582 | 596 | foreman |
583 | 597 | gemnasium-gitlab-service (~> 0.2) |
584 | - gemoji (~> 1.3.0) | |
585 | 598 | github-markup (~> 0.7.4)! |
586 | 599 | gitlab-flowdock-git-hook (~> 0.4.2) |
587 | 600 | gitlab-gollum-lib (~> 1.1.0) |
588 | 601 | gitlab-grack (~> 2.0.0.pre) |
589 | 602 | gitlab-linguist (~> 3.0.0) |
590 | - gitlab_git (~> 5.4.0) | |
603 | + gitlab_emoji (~> 0.0.1.1) | |
604 | + gitlab_git (~> 5.7.1) | |
591 | 605 | gitlab_meta (= 6.0) |
592 | 606 | gitlab_omniauth-ldap (= 1.0.4) |
593 | 607 | gon (~> 5.0.0) |
594 | 608 | grape (~> 0.6.1) |
595 | - grape-entity (~> 0.3.0) | |
609 | + grape-entity (~> 0.4.1)! | |
596 | 610 | growl |
597 | 611 | guard-rspec |
598 | 612 | guard-spinach |
... | ... | @@ -610,6 +624,7 @@ DEPENDENCIES |
610 | 624 | minitest (~> 4.7.0) |
611 | 625 | modernizr (= 2.6.2) |
612 | 626 | mysql2 |
627 | + nprogress-rails | |
613 | 628 | omniauth (~> 1.1.3) |
614 | 629 | omniauth-github |
615 | 630 | omniauth-google-oauth2 |
... | ... | @@ -642,9 +657,9 @@ DEPENDENCIES |
642 | 657 | simplecov |
643 | 658 | sinatra |
644 | 659 | six |
660 | + slack-notifier (~> 0.2.0) | |
645 | 661 | slim |
646 | 662 | spinach-rails |
647 | - spork (~> 1.0rc) | |
648 | 663 | spring (= 1.1.1) |
649 | 664 | spring-commands-rspec (= 1.0.1) |
650 | 665 | spring-commands-spinach (= 1.0.0) |
... | ... | @@ -659,4 +674,5 @@ DEPENDENCIES |
659 | 674 | underscore-rails (~> 1.4.4) |
660 | 675 | unicorn (~> 4.6.3) |
661 | 676 | unicorn-worker-killer |
677 | + version_sorter | |
662 | 678 | webmock | ... | ... |
PROCESS.md
... | ... | @@ -12,7 +12,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co |
12 | 12 | - Closes invalid issues and merge requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) |
13 | 13 | - Assigns appropriate [labels](#how-we-handle-issues) |
14 | 14 | - Asks for feedback from issue reporter/merge request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.) |
15 | -- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](http://gitlab.org/team/) | |
15 | +- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](https://www.gitlab.com/core-team/) | |
16 | 16 | - Monitors all issues/merge requests for feedback (but especially ones commented on since automatically watching them): |
17 | 17 | - Closes issues with no feedback from the reporter for two weeks |
18 | 18 | - Closes stale merge requests |
... | ... | @@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co |
24 | 24 | - Monitors for new merge requests (at least once a week) |
25 | 25 | - Manages their work queue by looking at issues and merge requests assigned to them |
26 | 26 | - Close fixed issues (via commit messages or manually) |
27 | -- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)! | |
28 | -- Response guidelines | |
29 | 27 | - Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to merge requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review). |
30 | 28 | |
31 | 29 | ## Priorities of the issue team |
... | ... | @@ -37,7 +35,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co |
37 | 35 | |
38 | 36 | ## Mentioning people |
39 | 37 | |
40 | -The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](http://gitlab.org/team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person. | |
38 | +The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](https://www.gitlab.com/core-team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person. | |
41 | 39 | |
42 | 40 | ## Workflow labels |
43 | 41 | |
... | ... | @@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue |
73 | 71 | |
74 | 72 | ### Feature requests |
75 | 73 | |
76 | -Thanks for your interest in GitLab. We don't use the issue tracker for feature requests. Please use http://feedback.gitlab.com/ for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. | |
74 | +Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. | |
77 | 75 | |
78 | 76 | ### Issue report for old version |
79 | 77 | |
... | ... | @@ -89,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea |
89 | 87 | |
90 | 88 | ### Issue fixed in newer version |
91 | 89 | |
92 | -Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(http://blog.gitlab.org/). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). | |
90 | +Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). | |
93 | 91 | |
94 | 92 | ### Improperly formatted merge request |
95 | 93 | ... | ... |
Procfile
README.md
... | ... | @@ -23,19 +23,17 @@ |
23 | 23 | |
24 | 24 | ### Resources |
25 | 25 | |
26 | -* GitLab.org community site: [Homepage](http://gitlab.org) | [Screenshots](http://gitlab.org/screenshots/) | [Blog](http://blog.gitlab.org/) | [Demo](http://demo.gitlabhq.com/users/sign_in) | |
26 | +* [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/). | |
27 | 27 | |
28 | -* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/) | |
28 | +* [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ce/) offers additional features that are useful for larger organizations (100+ users). | |
29 | 29 | |
30 | -* [GitLab Enterprise Edition](https://www.gitlab.com/features/) offers additional features that are useful for larger organizations (100+ users). | |
31 | - | |
32 | -* [GitLab CI](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab. | |
30 | +* [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab. | |
33 | 31 | |
34 | 32 | * Unofficial third-party [iPhone app](http://gitlabcontrol.com/) and [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) for GitLab |
35 | 33 | |
36 | 34 | ### Requirements |
37 | 35 | |
38 | -* Ubuntu/Debian** | |
36 | +* Ubuntu/Debian/CentOS/RHEL** | |
39 | 37 | * ruby 1.9.3+ |
40 | 38 | * git 1.7.10+ |
41 | 39 | * redis 2.0+ |
... | ... | @@ -47,7 +45,7 @@ |
47 | 45 | |
48 | 46 | #### Official installation methods |
49 | 47 | |
50 | -* [GitLab packages (beta)](https://www.gitlab.com/downloads/) These packages contain GitLab and all its depencies (PostgreSQL, Redis, Nginx, Unicorn, etc.). They are made with [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md) that also contains the installation instructions. These packages currently support a reduced selection of GitLab's normal features. For instance, it is not yet possible to create/restore application backups or to use HTTPS. | |
48 | +* [GitLab packages](https://www.gitlab.com/downloads/) These packages contain GitLab and all its depencies (Ruby, PostgreSQL, Redis, Nginx, Unicorn, etc.). They are made with [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md) that also contains the installation instructions. | |
51 | 49 | |
52 | 50 | * [GitLab virtual machine images](https://www.gitlab.com/downloads/) contain an operating system and a preinstalled GitLab. They are made with [GitLab Packer](https://gitlab.com/gitlab-org/gitlab-packer/blob/master/README.md) that also contains the installation instructions. |
53 | 51 | |
... | ... | @@ -73,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl |
73 | 71 | |
74 | 72 | It is recommended to follow a monthly upgrade schedule. Security releases come out when needed. For more information about the release process see the documentation for [monthly](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/monthly.md) and [security](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/security.md) releases. |
75 | 73 | |
76 | -* Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). | |
74 | +* Features that will be in the next releases are listed on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). | |
77 | 75 | |
78 | 76 | ### Run in production mode |
79 | 77 | |
... | ... | @@ -98,14 +96,9 @@ or start each component separately |
98 | 96 | |
99 | 97 | ### Run the tests |
100 | 98 | |
101 | -* Seed the database | |
102 | - | |
103 | - bundle exec rake db:setup RAILS_ENV=test | |
104 | - bundle exec rake db:seed_fu RAILS_ENV=test | |
105 | - | |
106 | 99 | * Run all tests |
107 | 100 | |
108 | - bundle exec rake gitlab:test RAILS_ENV=test | |
101 | + bundle exec rake test | |
109 | 102 | |
110 | 103 | * [RSpec](http://rspec.info/) unit and functional tests |
111 | 104 | |
... | ... | @@ -142,7 +135,7 @@ or start each component separately |
142 | 135 | |
143 | 136 | * [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. |
144 | 137 | |
145 | -* [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. | |
138 | +* [Feature request forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. | |
146 | 139 | |
147 | 140 | * [Contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) describes how to submit merge requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed. |
148 | 141 | |
... | ... | @@ -155,12 +148,3 @@ or start each component separately |
155 | 148 | * [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview. |
156 | 149 | |
157 | 150 | * [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help. |
158 | - | |
159 | - | |
160 | -### Getting in touch | |
161 | - | |
162 | -* [Core team](http://gitlab.org/team/) | |
163 | - | |
164 | -* [Contributors](http://contributors.gitlab.org/) | |
165 | - | |
166 | -* [Community](http://gitlab.org/community/) | ... | ... |
VERSION
1.86 KB
4.34 KB
1.57 KB
3.36 KB
1.38 KB
3.25 KB
2.91 KB
2.72 KB
494 Bytes
4.03 KB
4.09 KB
4.09 KB
app/assets/javascripts/application.js
... | ... | @@ -1,31 +0,0 @@ |
1 | -// This is a manifest file that'll be compiled into including all the files listed below. | |
2 | -// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically | |
3 | -// be included in the compiled file accessible from http://example.com/assets/application.js | |
4 | -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the | |
5 | -// the compiled file. | |
6 | -// | |
7 | -//= require jquery | |
8 | -//= require jquery.ui.all | |
9 | -//= require jquery_ujs | |
10 | -//= require jquery.cookie | |
11 | -//= require jquery.endless-scroll | |
12 | -//= require jquery.highlight | |
13 | -//= require jquery.history | |
14 | -//= require jquery.waitforimages | |
15 | -//= require jquery.atwho | |
16 | -//= require jquery.scrollto | |
17 | -//= require jquery.blockUI | |
18 | -//= require turbolinks | |
19 | -//= require jquery.turbolinks | |
20 | -//= require bootstrap | |
21 | -//= require modernizr | |
22 | -//= require select2 | |
23 | -//= require raphael | |
24 | -//= require g.raphael-min | |
25 | -//= require g.bar-min | |
26 | -//= require branch-graph | |
27 | -//= require highlightjs.min | |
28 | -//= require ace/ace | |
29 | -//= require_tree . | |
30 | -//= require d3 | |
31 | -//= require underscore |
... | ... | @@ -0,0 +1,154 @@ |
1 | +# This is a manifest file that'll be compiled into including all the files listed below. | |
2 | +# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically | |
3 | +# be included in the compiled file accessible from http://example.com/assets/application.js | |
4 | +# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the | |
5 | +# the compiled file. | |
6 | +# | |
7 | +#= require jquery | |
8 | +#= require jquery.ui.all | |
9 | +#= require jquery_ujs | |
10 | +#= require jquery.cookie | |
11 | +#= require jquery.endless-scroll | |
12 | +#= require jquery.highlight | |
13 | +#= require jquery.history | |
14 | +#= require jquery.waitforimages | |
15 | +#= require jquery.atwho | |
16 | +#= require jquery.scrollto | |
17 | +#= require jquery.blockUI | |
18 | +#= require turbolinks | |
19 | +#= require jquery.turbolinks | |
20 | +#= require bootstrap | |
21 | +#= require modernizr | |
22 | +#= require select2 | |
23 | +#= require raphael | |
24 | +#= require g.raphael-min | |
25 | +#= require g.bar-min | |
26 | +#= require branch-graph | |
27 | +#= require highlightjs.min | |
28 | +#= require ace/ace | |
29 | +#= require d3 | |
30 | +#= require underscore | |
31 | +#= require nprogress | |
32 | +#= require nprogress-turbolinks | |
33 | +#= require_tree . | |
34 | + | |
35 | +window.slugify = (text) -> | |
36 | + text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() | |
37 | + | |
38 | +window.ajaxGet = (url) -> | |
39 | + $.ajax({type: "GET", url: url, dataType: "script"}) | |
40 | + | |
41 | +window.showAndHide = (selector) -> | |
42 | + | |
43 | +window.errorMessage = (message) -> | |
44 | + ehtml = $("<p>") | |
45 | + ehtml.addClass("error_message") | |
46 | + ehtml.html(message) | |
47 | + ehtml | |
48 | + | |
49 | +window.split = (val) -> | |
50 | + return val.split( /,\s*/ ) | |
51 | + | |
52 | +window.extractLast = (term) -> | |
53 | + return split( term ).pop() | |
54 | + | |
55 | +# Disable button if text field is empty | |
56 | +window.disableButtonIfEmptyField = (field_selector, button_selector) -> | |
57 | + field = $(field_selector) | |
58 | + closest_submit = field.closest("form").find(button_selector) | |
59 | + | |
60 | + closest_submit.disable() if field.val() is "" | |
61 | + | |
62 | + field.on "input", -> | |
63 | + if $(@).val() is "" | |
64 | + closest_submit.disable() | |
65 | + else | |
66 | + closest_submit.enable() | |
67 | + | |
68 | +window.sanitize = (str) -> | |
69 | + return str.replace(/<(?:.|\n)*?>/gm, '') | |
70 | + | |
71 | +window.linkify = (str) -> | |
72 | + exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig | |
73 | + return str.replace(exp,"<a href='$1'>$1</a>") | |
74 | + | |
75 | +window.simpleFormat = (str) -> | |
76 | + linkify(sanitize(str).replace(/\n/g, '<br />')) | |
77 | + | |
78 | +window.unbindEvents = -> | |
79 | + $(document).unbind('scroll') | |
80 | + $(document).off('scroll') | |
81 | + | |
82 | +document.addEventListener("page:fetch", unbindEvents) | |
83 | + | |
84 | +$ -> | |
85 | + # Click a .one_click_select field, select the contents | |
86 | + $(".one_click_select").on 'click', -> $(@).select() | |
87 | + | |
88 | + $('.remove-row').bind 'ajax:success', -> | |
89 | + $(this).closest('li').fadeOut() | |
90 | + | |
91 | + # Initialize select2 selects | |
92 | + $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true) | |
93 | + | |
94 | + # Initialize tooltips | |
95 | + $('.has_tooltip').tooltip() | |
96 | + | |
97 | + # Bottom tooltip | |
98 | + $('.has_bottom_tooltip').tooltip(placement: 'bottom') | |
99 | + | |
100 | + # Form submitter | |
101 | + $('.trigger-submit').on 'change', -> | |
102 | + $(@).parents('form').submit() | |
103 | + | |
104 | + $("abbr.timeago").timeago() | |
105 | + $('.js-timeago').timeago() | |
106 | + | |
107 | + # Flash | |
108 | + if (flash = $(".flash-container")).length > 0 | |
109 | + flash.click -> $(@).fadeOut() | |
110 | + flash.show() | |
111 | + setTimeout (-> flash.fadeOut()), 5000 | |
112 | + | |
113 | + # Disable form buttons while a form is submitting | |
114 | + $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> | |
115 | + buttons = $('[type="submit"]', @) | |
116 | + | |
117 | + switch e.type | |
118 | + when 'ajax:beforeSend', 'submit' | |
119 | + buttons.disable() | |
120 | + else | |
121 | + buttons.enable() | |
122 | + | |
123 | + # Show/Hide the profile menu when hovering the account box | |
124 | + $('.account-box').hover -> $(@).toggleClass('hover') | |
125 | + | |
126 | + # Focus search field by pressing 's' key | |
127 | + $(document).keypress (e) -> | |
128 | + # Don't do anything if typing in an input | |
129 | + return if $(e.target).is(":input") | |
130 | + | |
131 | + switch e.which | |
132 | + when 115 | |
133 | + $("#search").focus() | |
134 | + e.preventDefault() | |
135 | + when 63 | |
136 | + new Shortcuts() | |
137 | + e.preventDefault() | |
138 | + | |
139 | + | |
140 | + # Commit show suppressed diff | |
141 | + $(".diff-content").on "click", ".supp_diff_link", -> | |
142 | + $(@).next('table').show() | |
143 | + $(@).remove() | |
144 | + | |
145 | +(($) -> | |
146 | + # Disable an element and add the 'disabled' Bootstrap class | |
147 | + $.fn.extend disable: -> | |
148 | + $(@).attr('disabled', 'disabled').addClass('disabled') | |
149 | + | |
150 | + # Enable an element and remove the 'disabled' Bootstrap class | |
151 | + $.fn.extend enable: -> | |
152 | + $(@).removeAttr('disabled').removeClass('disabled') | |
153 | + | |
154 | +)(jQuery) | ... | ... |
app/assets/javascripts/behaviors/details_behavior.coffee
1 | 1 | $ -> |
2 | 2 | $("body").on "click", ".js-details-target", -> |
3 | 3 | container = $(@).closest(".js-details-container") |
4 | - | |
5 | 4 | container.toggleClass("open") |
5 | + | |
6 | + # Show details content. Hides link after click. | |
7 | + # | |
8 | + # %div | |
9 | + # %a.js-details-expand | |
10 | + # %div.js-details-content | |
11 | + # | |
12 | + $("body").on "click", ".js-details-expand", (e) -> | |
13 | + $(@).next('.js-details-content').removeClass("hide") | |
14 | + $(@).hide() | |
15 | + e.preventDefault() | ... | ... |
app/assets/javascripts/behaviors/toggler_behavior.coffee
1 | 1 | $ -> |
2 | 2 | $("body").on "click", ".js-toggler-target", -> |
3 | 3 | container = $(@).closest(".js-toggler-container") |
4 | - | |
5 | 4 | container.toggleClass("on") |
6 | 5 | |
7 | - $("body").on "click", ".js-toggle-visibility-link", (e) -> | |
6 | + # Toggle button. Show/hide content inside parent container. | |
7 | + # Button does not change visibility. If button has icon - it changes chevron style. | |
8 | + # | |
9 | + # %div.js-toggle-container | |
10 | + # %a.js-toggle-button | |
11 | + # %div.js-toggle-content | |
12 | + # | |
13 | + $("body").on "click", ".js-toggle-button", (e) -> | |
8 | 14 | $(@).find('i'). |
9 | 15 | toggleClass('icon-chevron-down'). |
10 | 16 | toggleClass('icon-chevron-up') |
11 | - container = $(".js-toggle-visibility-container") | |
12 | - container.toggleClass("hide") | |
13 | - e.preventDefault() | |
14 | - | |
15 | - $("body").on "click", ".js-toggle-button", (e) -> | |
16 | 17 | $(@).closest(".js-toggle-container").find(".js-toggle-content").toggle() |
17 | 18 | e.preventDefault() | ... | ... |
app/assets/javascripts/gfm_auto_complete.js.coffee
... | ... | @@ -6,13 +6,13 @@ GitLab.GfmAutoComplete = |
6 | 6 | dataSource: '' |
7 | 7 | # Emoji |
8 | 8 | Emoji: |
9 | - assetBase: '' | |
10 | 9 | template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>' |
11 | 10 | |
12 | 11 | # Team Members |
13 | 12 | Members: |
14 | 13 | template: '<li data-value="${username}">${username} <small>${name}</small></li>' |
15 | 14 | |
15 | + # Issues and MergeRequests | |
16 | 16 | Issues: |
17 | 17 | template: '<li data-value="${id}"><small>${id}</small> ${title} </li>' |
18 | 18 | |
... | ... | @@ -26,7 +26,7 @@ GitLab.GfmAutoComplete = |
26 | 26 | tpl: @Emoji.template |
27 | 27 | callbacks: |
28 | 28 | before_save: (emojis) => |
29 | - $.map emojis, (em) => name: em, insert: em+ ':', image: "#{@Emoji.assetBase}/#{em}.png" | |
29 | + $.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path | |
30 | 30 | |
31 | 31 | # Team Members |
32 | 32 | input.atwho |
... | ... | @@ -46,11 +46,22 @@ GitLab.GfmAutoComplete = |
46 | 46 | before_save: (issues) -> |
47 | 47 | $.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}" |
48 | 48 | |
49 | + input.atwho | |
50 | + at: '!' | |
51 | + alias: 'mergerequests' | |
52 | + search_key: 'search' | |
53 | + tpl: @Issues.template | |
54 | + callbacks: | |
55 | + before_save: (merges) -> | |
56 | + $.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}" | |
57 | + | |
49 | 58 | input.one "focus", => |
50 | 59 | $.getJSON(@dataSource).done (data) -> |
51 | 60 | # load members |
52 | 61 | input.atwho 'load', "@", data.members |
53 | 62 | # load issues |
54 | 63 | input.atwho 'load', "issues", data.issues |
64 | + # load merge requests | |
65 | + input.atwho 'load', "mergerequests", data.mergerequests | |
55 | 66 | # load emojis |
56 | 67 | input.atwho 'load', ":", data.emojis | ... | ... |
app/assets/javascripts/main.js.coffee
... | ... | @@ -1,137 +0,0 @@ |
1 | -window.slugify = (text) -> | |
2 | - text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() | |
3 | - | |
4 | -window.ajaxGet = (url) -> | |
5 | - $.ajax({type: "GET", url: url, dataType: "script"}) | |
6 | - | |
7 | -window.showAndHide = (selector) -> | |
8 | - | |
9 | -window.errorMessage = (message) -> | |
10 | - ehtml = $("<p>") | |
11 | - ehtml.addClass("error_message") | |
12 | - ehtml.html(message) | |
13 | - ehtml | |
14 | - | |
15 | -window.split = (val) -> | |
16 | - return val.split( /,\s*/ ) | |
17 | - | |
18 | -window.extractLast = (term) -> | |
19 | - return split( term ).pop() | |
20 | - | |
21 | -# Disable button if text field is empty | |
22 | -window.disableButtonIfEmptyField = (field_selector, button_selector) -> | |
23 | - field = $(field_selector) | |
24 | - closest_submit = field.closest("form").find(button_selector) | |
25 | - | |
26 | - closest_submit.disable() if field.val() is "" | |
27 | - | |
28 | - field.on "input", -> | |
29 | - if $(@).val() is "" | |
30 | - closest_submit.disable() | |
31 | - else | |
32 | - closest_submit.enable() | |
33 | - | |
34 | -window.sanitize = (str) -> | |
35 | - return str.replace(/<(?:.|\n)*?>/gm, '') | |
36 | - | |
37 | -window.linkify = (str) -> | |
38 | - exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig | |
39 | - return str.replace(exp,"<a href='$1'>$1</a>") | |
40 | - | |
41 | -window.simpleFormat = (str) -> | |
42 | - linkify(sanitize(str).replace(/\n/g, '<br />')) | |
43 | - | |
44 | -window.startSpinner = -> | |
45 | - $('.turbolink-spinner').fadeIn() | |
46 | - | |
47 | -window.stopSpinner = -> | |
48 | - $('.turbolink-spinner').fadeOut() | |
49 | - | |
50 | -window.unbindEvents = -> | |
51 | - $(document).unbind('scroll') | |
52 | - $(document).off('scroll') | |
53 | - | |
54 | -document.addEventListener("page:fetch", startSpinner) | |
55 | -document.addEventListener("page:fetch", unbindEvents) | |
56 | -document.addEventListener("page:change", stopSpinner) | |
57 | - | |
58 | -$ -> | |
59 | - # Click a .one_click_select field, select the contents | |
60 | - $(".one_click_select").on 'click', -> $(@).select() | |
61 | - | |
62 | - $('.remove-row').bind 'ajax:success', -> | |
63 | - $(this).closest('li').fadeOut() | |
64 | - | |
65 | - # Click a .appear-link, appear-data fadeout | |
66 | - $(".appear-link").on 'click', (e) -> | |
67 | - $('.appear-data').fadeIn() | |
68 | - e.preventDefault() | |
69 | - | |
70 | - # Initialize select2 selects | |
71 | - $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true) | |
72 | - | |
73 | - # Initialize tooltips | |
74 | - $('.has_tooltip').tooltip() | |
75 | - | |
76 | - # Bottom tooltip | |
77 | - $('.has_bottom_tooltip').tooltip(placement: 'bottom') | |
78 | - | |
79 | - # Form submitter | |
80 | - $('.trigger-submit').on 'change', -> | |
81 | - $(@).parents('form').submit() | |
82 | - | |
83 | - $("abbr.timeago").timeago() | |
84 | - $('.js-timeago').timeago() | |
85 | - | |
86 | - # Flash | |
87 | - if (flash = $(".flash-container")).length > 0 | |
88 | - flash.click -> $(@).fadeOut() | |
89 | - flash.show() | |
90 | - setTimeout (-> flash.fadeOut()), 5000 | |
91 | - | |
92 | - # Disable form buttons while a form is submitting | |
93 | - $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> | |
94 | - buttons = $('[type="submit"]', @) | |
95 | - | |
96 | - switch e.type | |
97 | - when 'ajax:beforeSend', 'submit' | |
98 | - buttons.disable() | |
99 | - else | |
100 | - buttons.enable() | |
101 | - | |
102 | - # Show/Hide the profile menu when hovering the account box | |
103 | - $('.account-box').hover -> $(@).toggleClass('hover') | |
104 | - | |
105 | - # Focus search field by pressing 's' key | |
106 | - $(document).keypress (e) -> | |
107 | - # Don't do anything if typing in an input | |
108 | - return if $(e.target).is(":input") | |
109 | - | |
110 | - switch e.which | |
111 | - when 115 | |
112 | - $("#search").focus() | |
113 | - e.preventDefault() | |
114 | - when 63 | |
115 | - new Shortcuts() | |
116 | - e.preventDefault() | |
117 | - | |
118 | - | |
119 | - # Commit show suppressed diff | |
120 | - $(".diff-content").on "click", ".supp_diff_link", -> | |
121 | - $(@).next('table').show() | |
122 | - $(@).remove() | |
123 | - | |
124 | - $(".diff-content").on "click", ".js-details-expand", -> | |
125 | - $(@).next('.js-details-contain').removeClass("hide") | |
126 | - $(@).remove() | |
127 | - | |
128 | -(($) -> | |
129 | - # Disable an element and add the 'disabled' Bootstrap class | |
130 | - $.fn.extend disable: -> | |
131 | - $(@).attr('disabled', 'disabled').addClass('disabled') | |
132 | - | |
133 | - # Enable an element and remove the 'disabled' Bootstrap class | |
134 | - $.fn.extend enable: -> | |
135 | - $(@).removeAttr('disabled').removeClass('disabled') | |
136 | - | |
137 | -)(jQuery) |
app/assets/javascripts/project_users_select.js.coffee
... | ... | @@ -10,6 +10,16 @@ |
10 | 10 | query: (query) -> |
11 | 11 | Api.projectUsers project_id, query.term, (users) -> |
12 | 12 | data = { results: users } |
13 | + | |
14 | + nullUser = { | |
15 | + name: 'Unassigned', | |
16 | + avatar: null, | |
17 | + username: 'none', | |
18 | + id: '' | |
19 | + } | |
20 | + | |
21 | + data.results.unshift(nullUser) | |
22 | + | |
13 | 23 | query.callback(data) |
14 | 24 | |
15 | 25 | initSelection: (element, callback) -> |
... | ... | @@ -35,8 +45,13 @@ |
35 | 45 | else |
36 | 46 | avatar = gon.relative_url_root + "/assets/no_avatar.png" |
37 | 47 | |
48 | + if user.id == '' | |
49 | + avatarMarkup = '' | |
50 | + else | |
51 | + avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>" | |
52 | + | |
38 | 53 | "<div class='user-result'> |
39 | - <div class='user-image'><img class='avatar s24' src='#{avatar}'></div> | |
54 | + #{avatarMarkup} | |
40 | 55 | <div class='user-name'>#{user.name}</div> |
41 | 56 | <div class='user-username'>#{user.username}</div> |
42 | 57 | </div>" | ... | ... |
app/assets/stylesheets/application.scss
... | ... | @@ -7,6 +7,8 @@ |
7 | 7 | *= require select2 |
8 | 8 | *= require highlightjs.min |
9 | 9 | *= require_self |
10 | + *= require nprogress | |
11 | + *= require nprogress-bootstrap | |
10 | 12 | */ |
11 | 13 | |
12 | 14 | @import "main/variables.scss"; |
... | ... | @@ -70,7 +72,7 @@ |
70 | 72 | @import "sections/groups.scss"; |
71 | 73 | |
72 | 74 | /** |
73 | - * Code ighlight | |
75 | + * Code highlight | |
74 | 76 | */ |
75 | 77 | @import "highlight/white.scss"; |
76 | 78 | @import "highlight/dark.scss"; | ... | ... |
app/assets/stylesheets/generic/common.scss
... | ... | @@ -298,10 +298,6 @@ img.emoji { |
298 | 298 | width: 20px; |
299 | 299 | } |
300 | 300 | |
301 | -.appear-data { | |
302 | - display: none; | |
303 | -} | |
304 | - | |
305 | 301 | .chart { |
306 | 302 | overflow: hidden; |
307 | 303 | height: 220px; |
... | ... | @@ -359,3 +355,7 @@ table { |
359 | 355 | @media (max-width: $screen-xs-max) { |
360 | 356 | .container .content { margin-top: 20px; } |
361 | 357 | } |
358 | + | |
359 | +.wiki .highlight, .note-body .highlight { | |
360 | + margin-bottom: 9px; | |
361 | +} | ... | ... |
app/assets/stylesheets/generic/forms.scss
app/assets/stylesheets/generic/issue_box.scss
... | ... | @@ -10,9 +10,61 @@ |
10 | 10 | .issue-box { |
11 | 11 | color: #666; |
12 | 12 | margin:20px 0; |
13 | - background: #FAFAFA; | |
13 | + background: #FFF; | |
14 | 14 | border: 1px solid #EEE; |
15 | 15 | |
16 | + &.issue-box-closed { | |
17 | + border-color: #DA4E49; | |
18 | + .state { | |
19 | + background-color: #f2dede; | |
20 | + border-color: #ebccd1; | |
21 | + color: #a94442; | |
22 | + .state-label { | |
23 | + background: #DA4E49; | |
24 | + color: #FFF; | |
25 | + } | |
26 | + } | |
27 | + } | |
28 | + | |
29 | + &.issue-box-merged { | |
30 | + border-color: #31708f; | |
31 | + .state { | |
32 | + background-color: #d9edf7; | |
33 | + border-color: #bce8f1; | |
34 | + color: #31708f; | |
35 | + .state-label { | |
36 | + background: #31708f; | |
37 | + color: #FFF; | |
38 | + } | |
39 | + } | |
40 | + } | |
41 | + | |
42 | + &.issue-box-open { | |
43 | + border-color: #4A4; | |
44 | + .state { | |
45 | + background-color: #dff0d8; | |
46 | + border-color: #d6e9c6; | |
47 | + color: #3c763d; | |
48 | + .state-label { | |
49 | + background: #4A4; | |
50 | + color: #FFF; | |
51 | + } | |
52 | + } | |
53 | + } | |
54 | + | |
55 | + &.issue-box-expired { | |
56 | + border-color: #cea61b; | |
57 | + .state { | |
58 | + background-color: #fcf8e3; | |
59 | + border-color: #faebcc; | |
60 | + color: #8a6d3b; | |
61 | + .state-label { | |
62 | + background: #cea61b; | |
63 | + color: #FFF; | |
64 | + } | |
65 | + } | |
66 | + } | |
67 | + | |
16 | 68 | .control-group { |
17 | 69 | margin-bottom: 0; |
18 | 70 | } |
... | ... | @@ -51,24 +103,10 @@ |
51 | 103 | |
52 | 104 | .state-label { |
53 | 105 | font-size: 14px; |
54 | - padding: 9px 25px; | |
106 | + padding: 1px 25px; | |
55 | 107 | text-align: center; |
56 | 108 | text-shadow: none; |
57 | 109 | margin-right: 20px; |
58 | - | |
59 | - &.state-label-blue { | |
60 | - background: #31708f; | |
61 | - color: #FFF; | |
62 | - } | |
63 | - | |
64 | - &.state-label-green { | |
65 | - background: #4A4; | |
66 | - color: #FFF; | |
67 | - } | |
68 | - | |
69 | - &.state-label-red { | |
70 | - background: #DA4E49; | |
71 | - color: #FFF; | |
72 | - } | |
110 | + display: inline-block; | |
73 | 111 | } |
74 | 112 | } | ... | ... |
app/assets/stylesheets/generic/typography.scss
... | ... | @@ -88,9 +88,6 @@ a:focus { |
88 | 88 | .wiki { |
89 | 89 | @include md-typography; |
90 | 90 | |
91 | - font-size: 14px; | |
92 | - line-height: 1.6; | |
93 | - | |
94 | 91 | /* Link to current header. */ |
95 | 92 | h1, h2, h3, h4, h5, h6 { |
96 | 93 | position: relative; |
... | ... | @@ -105,7 +102,7 @@ a:focus { |
105 | 102 | display: inline-block; |
106 | 103 | width: $size; |
107 | 104 | height: $size; |
108 | - background-image: url("icon-link.png"); | |
105 | + background-image: image-url("icon-link.png"); | |
109 | 106 | background-size: contain; |
110 | 107 | background-repeat: no-repeat; |
111 | 108 | } |
... | ... | @@ -120,3 +117,11 @@ a:focus { |
120 | 117 | .md { |
121 | 118 | @include md-typography; |
122 | 119 | } |
120 | + | |
121 | +/** | |
122 | + * Textareas intended for GFM | |
123 | + * | |
124 | + */ | |
125 | +textarea.js-gfm-input { | |
126 | + font-family: $monospace_font; | |
127 | +} | ... | ... |
app/assets/stylesheets/highlight/white.scss
app/assets/stylesheets/main/layout.scss
app/assets/stylesheets/main/mixins.scss
... | ... | @@ -0,0 +1,13 @@ |
1 | +/* Generic print styles */ | |
2 | +header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;} | |
3 | +.profiler-results {display: none;} | |
4 | + | |
5 | +/* Styles targeted specifically at printing files */ | |
6 | +.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;} | |
7 | +.file-title {display: none;} | |
8 | +.file-holder {border: none;} | |
9 | + | |
10 | +.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; } | |
11 | +.wiki h1 {font-size: 30px;} | |
12 | +.wiki h2 {font-size: 22px;} | |
13 | +.wiki h3 {font-size: 18px; font-weight: bold; } | ... | ... |
app/assets/stylesheets/sections/commits.scss
... | ... | @@ -73,7 +73,7 @@ |
73 | 73 | |
74 | 74 | |
75 | 75 | .commits-compare-switch{ |
76 | - background: url("switch_icon.png") no-repeat center center; | |
76 | + background: image-url("switch_icon.png") no-repeat center center; | |
77 | 77 | width: 32px; |
78 | 78 | height: 32px; |
79 | 79 | text-indent: -9999px; |
... | ... | @@ -168,6 +168,25 @@ li.commit { |
168 | 168 | text-decoration: underline; |
169 | 169 | } |
170 | 170 | } |
171 | + | |
172 | + .text-expander { | |
173 | + background: #eee; | |
174 | + color: #555; | |
175 | + padding: 0 5px; | |
176 | + cursor: pointer; | |
177 | + margin-left: 4px; | |
178 | + &:hover { | |
179 | + background-color: #ddd; | |
180 | + } | |
181 | + } | |
182 | + } | |
183 | + | |
184 | + .commit-row-description { | |
185 | + font-size: 14px; | |
186 | + border-left: 1px solid #e5e5e5; | |
187 | + padding: 0 15px 0 7px; | |
188 | + margin: 5px 0 10px 5px; | |
189 | + display: none; | |
171 | 190 | } |
172 | 191 | |
173 | 192 | .commit-row-info { | ... | ... |
app/assets/stylesheets/sections/dashboard.scss
app/assets/stylesheets/sections/diff.scss
... | ... | @@ -144,7 +144,7 @@ |
144 | 144 | line-height: 0; |
145 | 145 | img{ |
146 | 146 | border: 1px solid #FFF; |
147 | - background: url('trans_bg.gif'); | |
147 | + background: image-url('trans_bg.gif'); | |
148 | 148 | max-width: 100%; |
149 | 149 | } |
150 | 150 | &.deleted { |
... | ... | @@ -209,7 +209,7 @@ |
209 | 209 | width: 15px; |
210 | 210 | position: absolute; |
211 | 211 | top: 0px; |
212 | - background: url('swipemode_sprites.gif') 0 3px no-repeat; | |
212 | + background: image-url('swipemode_sprites.gif') 0 3px no-repeat; | |
213 | 213 | } |
214 | 214 | .bottom-handle{ |
215 | 215 | display: block; |
... | ... | @@ -217,7 +217,7 @@ |
217 | 217 | width: 15px; |
218 | 218 | position: absolute; |
219 | 219 | bottom: 0px; |
220 | - background: url('swipemode_sprites.gif') 0 -11px no-repeat; | |
220 | + background: image-url('swipemode_sprites.gif') 0 -11px no-repeat; | |
221 | 221 | } |
222 | 222 | } |
223 | 223 | } //.view.swipe |
... | ... | @@ -249,7 +249,7 @@ |
249 | 249 | left: 12px; |
250 | 250 | height: 10px; |
251 | 251 | width: 276px; |
252 | - background: url('onion_skin_sprites.gif') -4px -20px repeat-x; | |
252 | + background: image-url('onion_skin_sprites.gif') -4px -20px repeat-x; | |
253 | 253 | } |
254 | 254 | |
255 | 255 | .dragger { |
... | ... | @@ -259,7 +259,7 @@ |
259 | 259 | top: 0px; |
260 | 260 | height: 14px; |
261 | 261 | width: 14px; |
262 | - background: url('onion_skin_sprites.gif') 0px -34px repeat-x; | |
262 | + background: image-url('onion_skin_sprites.gif') 0px -34px repeat-x; | |
263 | 263 | cursor: pointer; |
264 | 264 | } |
265 | 265 | |
... | ... | @@ -270,7 +270,7 @@ |
270 | 270 | right: 0px; |
271 | 271 | height: 10px; |
272 | 272 | width: 10px; |
273 | - background: url('onion_skin_sprites.gif') -2px 0px no-repeat; | |
273 | + background: image-url('onion_skin_sprites.gif') -2px 0px no-repeat; | |
274 | 274 | } |
275 | 275 | |
276 | 276 | .opaque { |
... | ... | @@ -280,7 +280,7 @@ |
280 | 280 | left: 0px; |
281 | 281 | height: 10px; |
282 | 282 | width: 10px; |
283 | - background: url('onion_skin_sprites.gif') -2px -10px no-repeat; | |
283 | + background: image-url('onion_skin_sprites.gif') -2px -10px no-repeat; | |
284 | 284 | } |
285 | 285 | } |
286 | 286 | } //.view.onion-skin | ... | ... |
app/assets/stylesheets/sections/events.scss
... | ... | @@ -42,7 +42,7 @@ |
42 | 42 | } |
43 | 43 | } |
44 | 44 | |
45 | - padding: 14px 0px; | |
45 | + padding: 12px 0px; | |
46 | 46 | border-bottom: 1px solid #eee; |
47 | 47 | .event-title { |
48 | 48 | color: #333; |
... | ... | @@ -63,6 +63,10 @@ |
63 | 63 | color: #666; |
64 | 64 | margin-top: 5px; |
65 | 65 | |
66 | + .md { | |
67 | + font-size: 13px; | |
68 | + } | |
69 | + | |
66 | 70 | pre { |
67 | 71 | border: none; |
68 | 72 | background: #f9f9f9; | ... | ... |
app/assets/stylesheets/sections/header.scss
... | ... | @@ -19,7 +19,7 @@ header { |
19 | 19 | line-height: 32px; |
20 | 20 | padding: 6px 10px; |
21 | 21 | |
22 | - &:hover { | |
22 | + &:hover, &:focus, &:active { | |
23 | 23 | background: none; |
24 | 24 | } |
25 | 25 | } |
... | ... | @@ -161,7 +161,7 @@ header { |
161 | 161 | } |
162 | 162 | |
163 | 163 | .search-input { |
164 | - background-image: url("icon-search.png"); | |
164 | + background-image: image-url("icon-search.png"); | |
165 | 165 | background-repeat: no-repeat; |
166 | 166 | background-position: 10px; |
167 | 167 | height: inherit; |
... | ... | @@ -192,7 +192,8 @@ header { |
192 | 192 | color: #AAA; |
193 | 193 | text-shadow: 0 1px 0 #444; |
194 | 194 | |
195 | - &:hover { | |
195 | + &:hover, &:focus, &:active { | |
196 | + background: none; | |
196 | 197 | color: #FFF; |
197 | 198 | } |
198 | 199 | } |
... | ... | @@ -272,3 +273,9 @@ header { |
272 | 273 | } |
273 | 274 | } |
274 | 275 | } |
276 | + | |
277 | +@media (max-width: $screen-xs-max) { | |
278 | + #nprogress .spinner { | |
279 | + right: 35px !important; | |
280 | + } | |
281 | +} | ... | ... |
app/assets/stylesheets/sections/merge_requests.scss
app/assets/stylesheets/sections/notes.scss
... | ... | @@ -27,11 +27,15 @@ ul.notes { |
27 | 27 | |
28 | 28 | .discussion-last-update, |
29 | 29 | .note-last-update { |
30 | - font-style: italic; | |
30 | + &:before { | |
31 | + content: "\00b7"; | |
32 | + } | |
33 | + font-size: 13px; | |
31 | 34 | } |
32 | 35 | .author { |
33 | - color: $style_color; | |
36 | + color: #555; | |
34 | 37 | font-weight: bold; |
38 | + font-size: 14px; | |
35 | 39 | &:hover { |
36 | 40 | color: $primary_color; |
37 | 41 | } |
... | ... | @@ -87,7 +91,6 @@ ul.notes { |
87 | 91 | } |
88 | 92 | .attachment { |
89 | 93 | font-size: 14px; |
90 | - margin-top: -20px; | |
91 | 94 | } |
92 | 95 | .note-body { |
93 | 96 | @include md-typography; |
... | ... | @@ -190,14 +193,13 @@ ul.notes { |
190 | 193 | } |
191 | 194 | |
192 | 195 | |
193 | - | |
194 | 196 | /** |
195 | 197 | * Line note button on the side of diffs |
196 | 198 | */ |
197 | 199 | |
198 | 200 | .diff-file tr.line_holder { |
199 | 201 | .add-diff-note { |
200 | - background: url("diff_note_add.png") no-repeat left 0; | |
202 | + background: image-url("diff_note_add.png") no-repeat left 0; | |
201 | 203 | height: 22px; |
202 | 204 | margin-left: -65px; |
203 | 205 | position: absolute; |
... | ... | @@ -305,6 +307,7 @@ ul.notes { |
305 | 307 | @extend .col-md-4; |
306 | 308 | @extend .thumbnail; |
307 | 309 | margin-left: 45px; |
310 | + float: none; | |
308 | 311 | } |
309 | 312 | |
310 | 313 | ... | ... |
app/controllers/admin/background_jobs_controller.rb
1 | 1 | class Admin::BackgroundJobsController < Admin::ApplicationController |
2 | 2 | def show |
3 | - @sidekiq_processes = `ps -U #{Settings.gitlab.user} -o euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep` | |
3 | + ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) | |
4 | + @sidekiq_processes = ps_output.split("\n").grep(/sidekiq/) | |
4 | 5 | end |
5 | 6 | end | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base |
6 | 6 | before_filter :check_password_expiration |
7 | 7 | around_filter :set_current_user_for_thread |
8 | 8 | before_filter :add_abilities |
9 | + before_filter :ldap_security_check | |
9 | 10 | before_filter :dev_tools if Rails.env == 'development' |
10 | 11 | before_filter :default_headers |
11 | 12 | before_filter :add_gon_variables |
... | ... | @@ -179,11 +180,30 @@ class ApplicationController < ActionController::Base |
179 | 180 | end |
180 | 181 | end |
181 | 182 | |
183 | + def ldap_security_check | |
184 | + if current_user && current_user.requires_ldap_check? | |
185 | + gitlab_ldap_access do |access| | |
186 | + if access.allowed?(current_user) | |
187 | + current_user.last_credential_check_at = Time.now | |
188 | + current_user.save | |
189 | + else | |
190 | + sign_out current_user | |
191 | + flash[:alert] = "Access denied for your LDAP account." | |
192 | + redirect_to new_user_session_path | |
193 | + end | |
194 | + end | |
195 | + end | |
196 | + end | |
197 | + | |
182 | 198 | def event_filter |
183 | 199 | filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? |
184 | 200 | @event_filter ||= EventFilter.new(filters) |
185 | 201 | end |
186 | 202 | |
203 | + def gitlab_ldap_access(&block) | |
204 | + Gitlab::LDAP::Access.open { |access| block.call(access) } | |
205 | + end | |
206 | + | |
187 | 207 | # JSON for infinite scroll via Pager object |
188 | 208 | def pager_json(partial, count) |
189 | 209 | html = render_to_string( |
... | ... | @@ -210,4 +230,8 @@ class ApplicationController < ActionController::Base |
210 | 230 | devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me) } |
211 | 231 | devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :name, :password, :password_confirmation) } |
212 | 232 | end |
233 | + | |
234 | + def hexdigest(string) | |
235 | + Digest::SHA1.hexdigest string | |
236 | + end | |
213 | 237 | end | ... | ... |
app/controllers/dashboard_controller.rb
... | ... | @@ -22,6 +22,8 @@ class DashboardController < ApplicationController |
22 | 22 | |
23 | 23 | @last_push = current_user.recent_push |
24 | 24 | |
25 | + @publicish_project_count = Project.publicish(current_user).count | |
26 | + | |
25 | 27 | respond_to do |format| |
26 | 28 | format.html |
27 | 29 | format.json { pager_json("events/_events", @events.count) } | ... | ... |
app/controllers/groups_controller.rb
... | ... | @@ -100,7 +100,7 @@ class GroupsController < ApplicationController |
100 | 100 | end |
101 | 101 | |
102 | 102 | def projects |
103 | - @projects ||= ProjectsFinder.new.execute(current_user, group: group) | |
103 | + @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity.non_archived | |
104 | 104 | end |
105 | 105 | |
106 | 106 | def project_ids | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +class PasswordsController < Devise::PasswordsController | |
2 | + | |
3 | + def create | |
4 | + email = resource_params[:email] | |
5 | + resource_found = resource_class.find_by_email(email) | |
6 | + if resource_found && resource_found.ldap_user? | |
7 | + flash[:alert] = "Cannot reset password for LDAP user." | |
8 | + respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return | |
9 | + end | |
10 | + | |
11 | + self.resource = resource_class.send_reset_password_instructions(resource_params) | |
12 | + if successfully_sent?(resource) | |
13 | + respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) | |
14 | + else | |
15 | + respond_with(resource) | |
16 | + end | |
17 | + end | |
18 | +end | ... | ... |
app/controllers/profiles/keys_controller.rb
... | ... | @@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController |
41 | 41 | begin |
42 | 42 | user = User.find_by_username(params[:username]) |
43 | 43 | if user.present? |
44 | - render text: user.all_ssh_keys.join("\n") | |
44 | + render text: user.all_ssh_keys.join("\n"), content_type: "text/plain" | |
45 | 45 | else |
46 | 46 | render_404 and return |
47 | 47 | end | ... | ... |
app/controllers/projects/edit_tree_controller.rb
... | ... | @@ -2,6 +2,8 @@ class Projects::EditTreeController < Projects::BaseTreeController |
2 | 2 | before_filter :require_branch_head |
3 | 3 | before_filter :blob |
4 | 4 | before_filter :authorize_push! |
5 | + before_filter :from_merge_request | |
6 | + before_filter :after_edit_path | |
5 | 7 | |
6 | 8 | def show |
7 | 9 | @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha |
... | ... | @@ -13,15 +15,11 @@ class Projects::EditTreeController < Projects::BaseTreeController |
13 | 15 | if result[:status] == :success |
14 | 16 | flash[:notice] = "Your changes have been successfully committed" |
15 | 17 | |
16 | - # If blob edit was initiated from merge request page | |
17 | - from_merge_request = MergeRequest.find_by(id: params[:from_merge_request_id]) | |
18 | - | |
19 | 18 | if from_merge_request |
20 | 19 | from_merge_request.reload_code |
21 | - redirect_to diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) | |
22 | - else | |
23 | - redirect_to project_blob_path(@project, @id) | |
24 | 20 | end |
21 | + | |
22 | + redirect_to after_edit_path | |
25 | 23 | else |
26 | 24 | flash[:alert] = result[:error] |
27 | 25 | render :show |
... | ... | @@ -33,4 +31,19 @@ class Projects::EditTreeController < Projects::BaseTreeController |
33 | 31 | def blob |
34 | 32 | @blob ||= @repository.blob_at(@commit.id, @path) |
35 | 33 | end |
34 | + | |
35 | + def after_edit_path | |
36 | + @after_edit_path ||= | |
37 | + if from_merge_request | |
38 | + diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) + | |
39 | + "#file-path-#{hexdigest(@path)}" | |
40 | + else | |
41 | + project_blob_path(@project, @id) | |
42 | + end | |
43 | + end | |
44 | + | |
45 | + def from_merge_request | |
46 | + # If blob edit was initiated from merge request page | |
47 | + @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id]) | |
48 | + end | |
36 | 49 | end | ... | ... |
app/controllers/projects/issues_controller.rb
... | ... | @@ -76,7 +76,7 @@ class Projects::IssuesController < Projects::ApplicationController |
76 | 76 | end |
77 | 77 | |
78 | 78 | def update |
79 | - @issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id)) | |
79 | + @issue.update_attributes(params[:issue]) | |
80 | 80 | @issue.reset_events_cache |
81 | 81 | |
82 | 82 | respond_to do |format| | ... | ... |
app/controllers/projects/merge_requests_controller.rb
... | ... | @@ -60,7 +60,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
60 | 60 | def new |
61 | 61 | @merge_request = MergeRequest.new(params[:merge_request]) |
62 | 62 | @merge_request.source_project = @project unless @merge_request.source_project |
63 | - @merge_request.target_project = @project unless @merge_request.target_project | |
63 | + @merge_request.target_project ||= (@project.forked_from_project || @project) | |
64 | + @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names | |
65 | + | |
66 | + @merge_request.target_branch ||= @merge_request.target_project.default_branch | |
67 | + | |
64 | 68 | @source_project = @merge_request.source_project |
65 | 69 | @merge_request |
66 | 70 | end |
... | ... | @@ -105,7 +109,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
105 | 109 | params[:merge_request].delete(:source_project_id) |
106 | 110 | params[:merge_request].delete(:target_project_id) |
107 | 111 | |
108 | - if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) | |
112 | + if @merge_request.update_attributes(params[:merge_request]) | |
109 | 113 | @merge_request.reset_events_cache |
110 | 114 | |
111 | 115 | respond_to do |format| |
... | ... | @@ -131,7 +135,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
131 | 135 | def automerge |
132 | 136 | return access_denied! unless allowed_to_merge? |
133 | 137 | |
134 | - if @merge_request.opened? && @merge_request.can_be_merged? | |
138 | + if @merge_request.open? && @merge_request.can_be_merged? | |
135 | 139 | @merge_request.should_remove_source_branch = params[:should_remove_source_branch] |
136 | 140 | @merge_request.automerge!(current_user, params[:merge_commit_message]) |
137 | 141 | @status = true |
... | ... | @@ -162,7 +166,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
162 | 166 | end |
163 | 167 | |
164 | 168 | def ci_status |
165 | - status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) | |
169 | + status = @merge_request.source_project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) | |
166 | 170 | response = {status: status} |
167 | 171 | |
168 | 172 | render json: response |
... | ... | @@ -226,7 +230,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
226 | 230 | |
227 | 231 | @merge_request_diff = @merge_request.merge_request_diff |
228 | 232 | @allowed_to_merge = allowed_to_merge? |
229 | - @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge | |
233 | + @show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge | |
230 | 234 | end |
231 | 235 | |
232 | 236 | def allowed_to_merge? | ... | ... |
app/controllers/projects/milestones_controller.rb
... | ... | @@ -38,7 +38,6 @@ class Projects::MilestonesController < Projects::ApplicationController |
38 | 38 | |
39 | 39 | def create |
40 | 40 | @milestone = @project.milestones.new(params[:milestone]) |
41 | - @milestone.author_id_of_changes = current_user.id | |
42 | 41 | |
43 | 42 | if @milestone.save |
44 | 43 | redirect_to project_milestone_path(@project, @milestone) |
... | ... | @@ -48,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController |
48 | 47 | end |
49 | 48 | |
50 | 49 | def update |
51 | - @milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id)) | |
50 | + @milestone.update_attributes(params[:milestone]) | |
52 | 51 | |
53 | 52 | respond_to do |format| |
54 | 53 | format.js | ... | ... |
app/controllers/projects_controller.rb
... | ... | @@ -5,7 +5,7 @@ class ProjectsController < ApplicationController |
5 | 5 | |
6 | 6 | # Authorize |
7 | 7 | before_filter :authorize_read_project!, except: [:index, :new, :create] |
8 | - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] | |
8 | + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] | |
9 | 9 | before_filter :require_non_empty_project, only: [:blob, :tree, :graph] |
10 | 10 | |
11 | 11 | layout 'navless', only: [:new, :create, :fork] |
... | ... | @@ -21,16 +21,9 @@ class ProjectsController < ApplicationController |
21 | 21 | |
22 | 22 | def create |
23 | 23 | @project = ::Projects::CreateService.new(current_user, params[:project]).execute |
24 | + flash[:notice] = 'Project was successfully created.' if @project.saved? | |
24 | 25 | |
25 | 26 | respond_to do |format| |
26 | - flash[:notice] = 'Project was successfully created.' if @project.saved? | |
27 | - format.html do | |
28 | - if @project.saved? | |
29 | - redirect_to @project | |
30 | - else | |
31 | - render "new" | |
32 | - end | |
33 | - end | |
34 | 27 | format.js |
35 | 28 | end |
36 | 29 | end |
... | ... | @@ -55,6 +48,11 @@ class ProjectsController < ApplicationController |
55 | 48 | end |
56 | 49 | |
57 | 50 | def show |
51 | + if @project.import_in_progress? | |
52 | + redirect_to import_project_path(@project) | |
53 | + return | |
54 | + end | |
55 | + | |
58 | 56 | return authenticate_user! unless @project.public? || current_user |
59 | 57 | |
60 | 58 | limit = (params[:limit] || 20).to_i |
... | ... | @@ -67,9 +65,7 @@ class ProjectsController < ApplicationController |
67 | 65 | if @project.empty_repo? |
68 | 66 | render "projects/empty", layout: user_layout |
69 | 67 | else |
70 | - if current_user | |
71 | - @last_push = current_user.recent_push(@project.id) | |
72 | - end | |
68 | + @last_push = current_user.recent_push(@project.id) if current_user | |
73 | 69 | render :show, layout: user_layout |
74 | 70 | end |
75 | 71 | end |
... | ... | @@ -77,6 +73,28 @@ class ProjectsController < ApplicationController |
77 | 73 | end |
78 | 74 | end |
79 | 75 | |
76 | + def import | |
77 | + if project.import_finished? | |
78 | + redirect_to @project | |
79 | + return | |
80 | + end | |
81 | + end | |
82 | + | |
83 | + def retry_import | |
84 | + unless @project.import_failed? | |
85 | + redirect_to import_project_path(@project) | |
86 | + end | |
87 | + | |
88 | + @project.import_url = params[:project][:import_url] | |
89 | + | |
90 | + if @project.save | |
91 | + @project.reload | |
92 | + @project.import_retry | |
93 | + end | |
94 | + | |
95 | + redirect_to import_project_path(@project) | |
96 | + end | |
97 | + | |
80 | 98 | def destroy |
81 | 99 | return access_denied! unless can?(current_user, :remove_project, project) |
82 | 100 | |
... | ... | @@ -105,10 +123,20 @@ class ProjectsController < ApplicationController |
105 | 123 | end |
106 | 124 | |
107 | 125 | def autocomplete_sources |
126 | + note_type = params['type'] | |
127 | + note_id = params['type_id'] | |
128 | + participating = if note_type && note_id | |
129 | + participants_in(note_type, note_id) | |
130 | + else | |
131 | + [] | |
132 | + end | |
133 | + team_members = sorted(@project.team.members) | |
134 | + participants = team_members + participating | |
108 | 135 | @suggestions = { |
109 | - emojis: Emoji.names, | |
136 | + emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } }, | |
110 | 137 | issues: @project.issues.select([:iid, :title, :description]), |
111 | - members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } } | |
138 | + mergerequests: @project.merge_requests.select([:iid, :title, :description]), | |
139 | + members: participants.uniq | |
112 | 140 | } |
113 | 141 | |
114 | 142 | respond_to do |format| |
... | ... | @@ -143,4 +171,25 @@ class ProjectsController < ApplicationController |
143 | 171 | def user_layout |
144 | 172 | current_user ? "projects" : "public_projects" |
145 | 173 | end |
174 | + | |
175 | + def participants_in(type, id) | |
176 | + users = case type | |
177 | + when "Issue" | |
178 | + issue = @project.issues.find_by_iid(id) | |
179 | + issue ? issue.participants : [] | |
180 | + when "MergeRequest" | |
181 | + merge_request = @project.merge_requests.find_by_iid(id) | |
182 | + merge_request ? merge_request.participants : [] | |
183 | + when "Commit" | |
184 | + author_ids = Note.for_commit_id(id).pluck(:author_id).uniq | |
185 | + User.where(id: author_ids) | |
186 | + else | |
187 | + [] | |
188 | + end | |
189 | + sorted(users) | |
190 | + end | |
191 | + | |
192 | + def sorted(users) | |
193 | + users.uniq.sort_by(&:username).map { |user| { username: user.username, name: user.name } } | |
194 | + end | |
146 | 195 | end | ... | ... |
app/controllers/search_controller.rb
... | ... | @@ -7,6 +7,7 @@ class SearchController < ApplicationController |
7 | 7 | |
8 | 8 | if @project |
9 | 9 | return access_denied! unless can?(current_user, :download_code, @project) |
10 | + | |
10 | 11 | @search_results = Search::ProjectService.new(@project, current_user, params).execute |
11 | 12 | else |
12 | 13 | @search_results = Search::GlobalService.new(current_user, params).execute | ... | ... |
app/controllers/snippets_controller.rb
... | ... | @@ -19,6 +19,9 @@ class SnippetsController < ApplicationController |
19 | 19 | |
20 | 20 | def user_index |
21 | 21 | @user = User.find_by(username: params[:username]) |
22 | + | |
23 | + render_404 and return unless @user | |
24 | + | |
22 | 25 | @snippets = @user.snippets.fresh.non_expired |
23 | 26 | |
24 | 27 | if @user == current_user | ... | ... |
app/finders/base_finder.rb
... | ... | @@ -47,9 +47,9 @@ class BaseFinder |
47 | 47 | [] |
48 | 48 | end |
49 | 49 | elsif current_user && params[:authorized_only].presence |
50 | - klass.of_projects(current_user.authorized_projects) | |
50 | + klass.of_projects(current_user.authorized_projects).references(:project) | |
51 | 51 | else |
52 | - klass.of_projects(Project.accessible_to(current_user)) | |
52 | + klass.of_projects(Project.accessible_to(current_user)).references(:project) | |
53 | 53 | end |
54 | 54 | end |
55 | 55 | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -54,7 +54,7 @@ module ApplicationHelper |
54 | 54 | if group && group.avatar.present? |
55 | 55 | group.avatar.url |
56 | 56 | else |
57 | - '/assets/no_group_avatar.png' | |
57 | + image_path('no_group_avatar.png') | |
58 | 58 | end |
59 | 59 | end |
60 | 60 | |
... | ... | @@ -89,16 +89,15 @@ module ApplicationHelper |
89 | 89 | "Never" |
90 | 90 | end |
91 | 91 | |
92 | - def grouped_options_refs(destination = :tree) | |
92 | + def grouped_options_refs | |
93 | 93 | repository = @project.repository |
94 | 94 | |
95 | 95 | options = [ |
96 | 96 | ["Branches", repository.branch_names], |
97 | - ["Tags", repository.tag_names] | |
97 | + ["Tags", VersionSorter.rsort(repository.tag_names)] | |
98 | 98 | ] |
99 | 99 | |
100 | - # If reference is commit id - | |
101 | - # we should add it to branch/tag selectbox | |
100 | + # If reference is commit id - we should add it to branch/tag selectbox | |
102 | 101 | if(@ref && !options.flatten.include?(@ref) && |
103 | 102 | @ref =~ /^[0-9a-zA-Z]{6,52}$/) |
104 | 103 | options << ["Commit", [@ref]] |
... | ... | @@ -147,8 +146,7 @@ module ApplicationHelper |
147 | 146 | |
148 | 147 | def authbutton(provider, size = 64) |
149 | 148 | file_name = "#{provider.to_s.split('_').first}_#{size}.png" |
150 | - image_tag("authbuttons/#{file_name}", | |
151 | - alt: "Sign in with #{provider.to_s.titleize}") | |
149 | + image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}") | |
152 | 150 | end |
153 | 151 | |
154 | 152 | def simple_sanitize(str) | ... | ... |
app/helpers/gitlab_markdown_helper.rb
... | ... | @@ -35,7 +35,6 @@ module GitlabMarkdownHelper |
35 | 35 | # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- |
36 | 36 | filter_html: true, |
37 | 37 | with_toc_data: true, |
38 | - hard_wrap: true, | |
39 | 38 | safe_links_only: true |
40 | 39 | }.merge(options)) |
41 | 40 | @markdown = Redcarpet::Markdown.new(gitlab_renderer, |
... | ... | @@ -45,7 +44,7 @@ module GitlabMarkdownHelper |
45 | 44 | fenced_code_blocks: true, |
46 | 45 | autolink: true, |
47 | 46 | strikethrough: true, |
48 | - lax_html_blocks: true, | |
47 | + lax_spacing: true, | |
49 | 48 | space_after_headers: true, |
50 | 49 | superscript: true) |
51 | 50 | end |
... | ... | @@ -64,8 +63,7 @@ module GitlabMarkdownHelper |
64 | 63 | # project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq |
65 | 64 | # ref - name of the branch or reference, eg. stable |
66 | 65 | # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from |
67 | - # wiki - whether the markdown is from wiki or not | |
68 | - def create_relative_links(text, project, ref, requested_path, wiki = false) | |
66 | + def create_relative_links(text, project, ref, requested_path) | |
69 | 67 | @path_to_satellite = project.satellite.path |
70 | 68 | project_path_with_namespace = project.path_with_namespace |
71 | 69 | paths = extract_paths(text) |
... | ... | @@ -135,12 +133,12 @@ module GitlabMarkdownHelper |
135 | 133 | end |
136 | 134 | |
137 | 135 | # Checks if the path exists in the repo |
138 | - # eg. checks if doc/README.md exists, if it doesn't then it is a wiki link | |
136 | + # eg. checks if doc/README.md exists, if not then link to blob | |
139 | 137 | def path_with_ref(path, ref) |
140 | 138 | if file_exists?(path) |
141 | 139 | "#{local_path(path)}/#{correct_ref(ref)}" |
142 | 140 | else |
143 | - "wikis" | |
141 | + "blob/#{correct_ref(ref)}" | |
144 | 142 | end |
145 | 143 | end |
146 | 144 | ... | ... |
app/helpers/issues_helper.rb
... | ... | @@ -85,11 +85,15 @@ module IssuesHelper |
85 | 85 | options_from_collection_for_select(@project.milestones.active, 'id', 'title', object.milestone_id) |
86 | 86 | end |
87 | 87 | |
88 | - def issue_alert_class(issue) | |
89 | - if issue.closed? | |
90 | - 'alert-danger' | |
88 | + def issue_box_class(item) | |
89 | + if item.respond_to?(:expired?) && item.expired? | |
90 | + 'issue-box-expired' | |
91 | + elsif item.respond_to?(:merged?) && item.merged? | |
92 | + 'issue-box-merged' | |
93 | + elsif item.closed? | |
94 | + 'issue-box-closed' | |
91 | 95 | else |
92 | - 'alert-success' | |
96 | + 'issue-box-open' | |
93 | 97 | end |
94 | 98 | end |
95 | 99 | end | ... | ... |
app/helpers/merge_requests_helper.rb
1 | 1 | module MergeRequestsHelper |
2 | 2 | def new_mr_path_from_push_event(event) |
3 | + target_project = event.project.forked_from_project || event.project | |
3 | 4 | new_project_merge_request_path( |
4 | 5 | event.project, |
5 | - new_mr_from_push_event(event, event.project) | |
6 | + new_mr_from_push_event(event, target_project) | |
6 | 7 | ) |
7 | 8 | end |
8 | 9 | |
... | ... | @@ -19,7 +20,7 @@ module MergeRequestsHelper |
19 | 20 | target_project_id: target_project.id, |
20 | 21 | source_branch: event.branch_name, |
21 | 22 | target_branch: target_project.repository.root_ref, |
22 | - title: event.branch_name.titleize | |
23 | + title: event.branch_name.humanize | |
23 | 24 | } |
24 | 25 | end |
25 | 26 | |
... | ... | @@ -41,14 +42,4 @@ module MergeRequestsHelper |
41 | 42 | "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" |
42 | 43 | end |
43 | 44 | end |
44 | - | |
45 | - def merge_request_alert_class(merge_request) | |
46 | - if merge_request.merged? | |
47 | - 'alert-info' | |
48 | - elsif merge_request.closed? | |
49 | - 'alert-danger' | |
50 | - else | |
51 | - 'alert-success' | |
52 | - end | |
53 | - end | |
54 | 45 | end | ... | ... |
app/helpers/profile_helper.rb
app/helpers/tree_helper.rb
... | ... | @@ -40,7 +40,7 @@ module TreeHelper |
40 | 40 | # Returns boolean |
41 | 41 | def markup?(filename) |
42 | 42 | filename.downcase.end_with?(*%w(.textile .rdoc .org .creole |
43 | - .mediawiki .rst .asciidoc .pod)) | |
43 | + .mediawiki .rst .adoc .asciidoc .pod)) | |
44 | 44 | end |
45 | 45 | |
46 | 46 | def gitlab_markdown?(filename) | ... | ... |
app/mailers/emails/groups.rb
... | ... | @@ -3,7 +3,7 @@ module Emails |
3 | 3 | def group_access_granted_email(user_group_id) |
4 | 4 | @membership = UsersGroup.find(user_group_id) |
5 | 5 | @group = @membership.group |
6 | - | |
6 | + @target_url = group_url(@group) | |
7 | 7 | mail(to: @membership.user.email, |
8 | 8 | subject: subject("Access to group was granted")) |
9 | 9 | end | ... | ... |
app/mailers/emails/issues.rb
... | ... | @@ -3,6 +3,7 @@ module Emails |
3 | 3 | def new_issue_email(recipient_id, issue_id) |
4 | 4 | @issue = Issue.find(issue_id) |
5 | 5 | @project = @issue.project |
6 | + @target_url = project_issue_url(@project, @issue) | |
6 | 7 | mail(from: sender(@issue.author_id), |
7 | 8 | to: recipient(recipient_id), |
8 | 9 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -12,6 +13,7 @@ module Emails |
12 | 13 | @issue = Issue.find(issue_id) |
13 | 14 | @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id |
14 | 15 | @project = @issue.project |
16 | + @target_url = project_issue_url(@project, @issue) | |
15 | 17 | mail(from: sender(updated_by_user_id), |
16 | 18 | to: recipient(recipient_id), |
17 | 19 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -21,6 +23,7 @@ module Emails |
21 | 23 | @issue = Issue.find issue_id |
22 | 24 | @project = @issue.project |
23 | 25 | @updated_by = User.find updated_by_user_id |
26 | + @target_url = project_issue_url(@project, @issue) | |
24 | 27 | mail(from: sender(updated_by_user_id), |
25 | 28 | to: recipient(recipient_id), |
26 | 29 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -31,6 +34,7 @@ module Emails |
31 | 34 | @issue_status = status |
32 | 35 | @project = @issue.project |
33 | 36 | @updated_by = User.find updated_by_user_id |
37 | + @target_url = project_issue_url(@project, @issue) | |
34 | 38 | mail(from: sender(updated_by_user_id), |
35 | 39 | to: recipient(recipient_id), |
36 | 40 | subject: subject("#{@issue.title} (##{@issue.iid})")) | ... | ... |
app/mailers/emails/merge_requests.rb
... | ... | @@ -3,6 +3,7 @@ module Emails |
3 | 3 | def new_merge_request_email(recipient_id, merge_request_id) |
4 | 4 | @merge_request = MergeRequest.find(merge_request_id) |
5 | 5 | @project = @merge_request.project |
6 | + @target_url = project_merge_request_url(@project, @merge_request) | |
6 | 7 | mail(from: sender(@merge_request.author_id), |
7 | 8 | to: recipient(recipient_id), |
8 | 9 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -12,6 +13,7 @@ module Emails |
12 | 13 | @merge_request = MergeRequest.find(merge_request_id) |
13 | 14 | @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id |
14 | 15 | @project = @merge_request.project |
16 | + @target_url = project_merge_request_url(@project, @merge_request) | |
15 | 17 | mail(from: sender(updated_by_user_id), |
16 | 18 | to: recipient(recipient_id), |
17 | 19 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -21,15 +23,17 @@ module Emails |
21 | 23 | @merge_request = MergeRequest.find(merge_request_id) |
22 | 24 | @updated_by = User.find updated_by_user_id |
23 | 25 | @project = @merge_request.project |
26 | + @target_url = project_merge_request_url(@project, @merge_request) | |
24 | 27 | mail(from: sender(updated_by_user_id), |
25 | 28 | to: recipient(recipient_id), |
26 | 29 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
27 | 30 | end |
28 | 31 | |
29 | - def merged_merge_request_email(recipient_id, merge_request_id) | |
32 | + def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) | |
30 | 33 | @merge_request = MergeRequest.find(merge_request_id) |
31 | 34 | @project = @merge_request.project |
32 | - mail(from: sender(@merge_request.author_id_of_changes), | |
35 | + @target_url = project_merge_request_url(@project, @merge_request) | |
36 | + mail(from: sender(updated_by_user_id), | |
33 | 37 | to: recipient(recipient_id), |
34 | 38 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
35 | 39 | end | ... | ... |
app/mailers/emails/notes.rb
... | ... | @@ -4,6 +4,7 @@ module Emails |
4 | 4 | @note = Note.find(note_id) |
5 | 5 | @commit = @note.noteable |
6 | 6 | @project = @note.project |
7 | + @target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}") | |
7 | 8 | mail(from: sender(@note.author_id), |
8 | 9 | to: recipient(recipient_id), |
9 | 10 | subject: subject("#{@commit.title} (#{@commit.short_id})")) |
... | ... | @@ -13,6 +14,7 @@ module Emails |
13 | 14 | @note = Note.find(note_id) |
14 | 15 | @issue = @note.noteable |
15 | 16 | @project = @note.project |
17 | + @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}") | |
16 | 18 | mail(from: sender(@note.author_id), |
17 | 19 | to: recipient(recipient_id), |
18 | 20 | subject: subject("#{@issue.title} (##{@issue.iid})")) |
... | ... | @@ -22,6 +24,7 @@ module Emails |
22 | 24 | @note = Note.find(note_id) |
23 | 25 | @merge_request = @note.noteable |
24 | 26 | @project = @note.project |
27 | + @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") | |
25 | 28 | mail(from: sender(@note.author_id), |
26 | 29 | to: recipient(recipient_id), |
27 | 30 | subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) |
... | ... | @@ -30,6 +33,7 @@ module Emails |
30 | 33 | def note_wall_email(recipient_id, note_id) |
31 | 34 | @note = Note.find(note_id) |
32 | 35 | @project = @note.project |
36 | + @target_url = project_wall_url(@note.project, anchor: "note_#{@note.id}") | |
33 | 37 | mail(from: sender(@note.author_id), |
34 | 38 | to: recipient(recipient_id), |
35 | 39 | subject: subject("Note on wall")) | ... | ... |
app/mailers/emails/profile.rb
... | ... | @@ -3,6 +3,7 @@ module Emails |
3 | 3 | def new_user_email(user_id, password) |
4 | 4 | @user = User.find(user_id) |
5 | 5 | @password = password |
6 | + @target_url = user_url(@user) | |
6 | 7 | mail(to: @user.email, subject: subject("Account was created for you")) |
7 | 8 | end |
8 | 9 | |
... | ... | @@ -15,6 +16,7 @@ module Emails |
15 | 16 | def new_ssh_key_email(key_id) |
16 | 17 | @key = Key.find(key_id) |
17 | 18 | @user = @key.user |
19 | + @target_url = user_url(@user) | |
18 | 20 | mail(to: @user.email, subject: subject("SSH key was added to your account")) |
19 | 21 | end |
20 | 22 | end | ... | ... |
app/mailers/emails/projects.rb
... | ... | @@ -3,6 +3,7 @@ module Emails |
3 | 3 | def project_access_granted_email(user_project_id) |
4 | 4 | @users_project = UsersProject.find user_project_id |
5 | 5 | @project = @users_project.project |
6 | + @target_url = project_url(@project) | |
6 | 7 | mail(to: @users_project.user.email, |
7 | 8 | subject: subject("Access to project was granted")) |
8 | 9 | end |
... | ... | @@ -10,6 +11,7 @@ module Emails |
10 | 11 | def project_was_moved_email(project_id, user_id) |
11 | 12 | @user = User.find user_id |
12 | 13 | @project = Project.find project_id |
14 | + @target_url = project_url(@project) | |
13 | 15 | mail(to: @user.email, |
14 | 16 | subject: subject("Project was moved")) |
15 | 17 | end |
... | ... | @@ -21,6 +23,11 @@ module Emails |
21 | 23 | @commits = Commit.decorate(compare.commits) |
22 | 24 | @diffs = compare.diffs |
23 | 25 | @branch = branch |
26 | + if @commits.length > 1 | |
27 | + @target_url = project_compare_url(@project, from: @commits.first, to: @commits.last) | |
28 | + else | |
29 | + @target_url = project_commit_url(@project, @compare.commit) | |
30 | + end | |
24 | 31 | |
25 | 32 | mail(from: sender(author_id), |
26 | 33 | to: recipient, | ... | ... |
app/models/ability.rb
... | ... | @@ -240,6 +240,7 @@ class Ability |
240 | 240 | can_manage = group_abilities(user, group).include?(:manage_group) |
241 | 241 | if can_manage && (user != target_user) |
242 | 242 | rules << :modify |
243 | + rules << :destroy | |
243 | 244 | end |
244 | 245 | if !group.last_owner?(user) && (can_manage || (user == target_user)) |
245 | 246 | rules << :destroy | ... | ... |
app/models/commit.rb
... | ... | @@ -99,14 +99,16 @@ class Commit |
99 | 99 | # |
100 | 100 | # cut off, ellipses (`&hellp;`) are prepended to the commit message. |
101 | 101 | def description |
102 | - description = safe_message | |
102 | + title_end = safe_message.index(/\n/) | |
103 | + @description ||= if (!title_end && safe_message.length > 100) || (title_end && title_end > 100) | |
104 | + "…".html_safe << safe_message[80..-1] | |
105 | + else | |
106 | + safe_message.split(/\n/, 2)[1].try(:chomp) | |
107 | + end | |
108 | + end | |
103 | 109 | |
104 | - title_end = description.index(/\n/) | |
105 | - if (!title_end && description.length > 100) || (title_end && title_end > 100) | |
106 | - "…".html_safe << description[80..-1] | |
107 | - else | |
108 | - description.split(/\n/, 2)[1].try(:chomp) | |
109 | - end | |
110 | + def description? | |
111 | + description.present? | |
110 | 112 | end |
111 | 113 | |
112 | 114 | # Regular expression that identifies commit message clauses that trigger issue closing. | ... | ... |
app/models/concerns/issuable.rb
... | ... | @@ -23,7 +23,8 @@ module Issuable |
23 | 23 | scope :assigned, -> { where("assignee_id IS NOT NULL") } |
24 | 24 | scope :unassigned, -> { where("assignee_id IS NULL") } |
25 | 25 | scope :of_projects, ->(ids) { where(project_id: ids) } |
26 | - | |
26 | + scope :opened, -> { with_state(:opened, :reopened) } | |
27 | + scope :closed, -> { with_state(:closed) } | |
27 | 28 | |
28 | 29 | delegate :name, |
29 | 30 | :email, |
... | ... | @@ -36,8 +37,6 @@ module Issuable |
36 | 37 | allow_nil: true, |
37 | 38 | prefix: true |
38 | 39 | |
39 | - attr_accessor :author_id_of_changes | |
40 | - | |
41 | 40 | attr_mentionable :title, :description |
42 | 41 | end |
43 | 42 | ... | ... |
app/models/event.rb
... | ... | @@ -47,14 +47,6 @@ class Event < ActiveRecord::Base |
47 | 47 | scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } |
48 | 48 | |
49 | 49 | class << self |
50 | - def determine_action(record) | |
51 | - if [Issue, MergeRequest].include? record.class | |
52 | - Event::CREATED | |
53 | - elsif record.kind_of? Note | |
54 | - Event::COMMENTED | |
55 | - end | |
56 | - end | |
57 | - | |
58 | 50 | def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads') |
59 | 51 | commit = project.repository.commit(ref.target) |
60 | 52 | ... | ... |
app/models/issue.rb
... | ... | @@ -28,12 +28,9 @@ class Issue < ActiveRecord::Base |
28 | 28 | |
29 | 29 | scope :of_group, ->(group) { where(project_id: group.project_ids) } |
30 | 30 | scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } |
31 | - scope :opened, -> { with_state(:opened, :reopened) } | |
32 | - scope :closed, -> { with_state(:closed) } | |
33 | 31 | |
34 | 32 | attr_accessible :title, :assignee_id, :position, :description, |
35 | - :milestone_id, :label_list, :author_id_of_changes, | |
36 | - :state_event | |
33 | + :milestone_id, :label_list, :state_event | |
37 | 34 | |
38 | 35 | acts_as_taggable_on :labels |
39 | 36 | |
... | ... | @@ -50,9 +47,7 @@ class Issue < ActiveRecord::Base |
50 | 47 | end |
51 | 48 | |
52 | 49 | state :opened |
53 | - | |
54 | 50 | state :reopened |
55 | - | |
56 | 51 | state :closed |
57 | 52 | end |
58 | 53 | ... | ... |
app/models/key.rb
... | ... | @@ -53,7 +53,7 @@ class Key < ActiveRecord::Base |
53 | 53 | Tempfile.open('gitlab_key_file') do |file| |
54 | 54 | file.puts key |
55 | 55 | file.rewind |
56 | - cmd_output, cmd_status = popen("ssh-keygen -lf #{file.path}", '/tmp') | |
56 | + cmd_output, cmd_status = popen(%W(ssh-keygen -lf #{file.path}), '/tmp') | |
57 | 57 | end |
58 | 58 | |
59 | 59 | if cmd_status.zero? | ... | ... |
app/models/merge_request.rb
... | ... | @@ -38,7 +38,7 @@ class MergeRequest < ActiveRecord::Base |
38 | 38 | |
39 | 39 | delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil |
40 | 40 | |
41 | - attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event, :description | |
41 | + attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description | |
42 | 42 | |
43 | 43 | attr_accessor :should_remove_source_branch |
44 | 44 | |
... | ... | @@ -100,8 +100,6 @@ class MergeRequest < ActiveRecord::Base |
100 | 100 | |
101 | 101 | scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) } |
102 | 102 | scope :of_user_team, ->(team) { where("(source_project_id in (:team_project_ids) OR target_project_id in (:team_project_ids) AND assignee_id in (:team_member_ids))", team_project_ids: team.project_ids, team_member_ids: team.member_ids) } |
103 | - scope :opened, -> { with_state(:opened) } | |
104 | - scope :closed, -> { with_state(:closed) } | |
105 | 103 | scope :merged, -> { with_state(:merged) } |
106 | 104 | scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } |
107 | 105 | scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } |
... | ... | @@ -135,7 +133,7 @@ class MergeRequest < ActiveRecord::Base |
135 | 133 | end |
136 | 134 | |
137 | 135 | def reload_code |
138 | - if merge_request_diff && opened? | |
136 | + if merge_request_diff && open? | |
139 | 137 | merge_request_diff.reload_content |
140 | 138 | end |
141 | 139 | end |
... | ... | @@ -160,6 +158,10 @@ class MergeRequest < ActiveRecord::Base |
160 | 158 | MergeRequests::AutoMergeService.new.execute(self, current_user, commit_message) |
161 | 159 | end |
162 | 160 | |
161 | + def open? | |
162 | + opened? || reopened? | |
163 | + end | |
164 | + | |
163 | 165 | def mr_and_commit_notes |
164 | 166 | # Fetch comments only from last 100 commits |
165 | 167 | commits_for_notes_limit = 100 | ... | ... |
app/models/merge_request_diff.rb
... | ... | @@ -69,12 +69,6 @@ class MergeRequestDiff < ActiveRecord::Base |
69 | 69 | end |
70 | 70 | end |
71 | 71 | |
72 | - # When Git::Diff is not able to get diff | |
73 | - # because of git timeout it return this value | |
74 | - def broken_diffs | |
75 | - [Gitlab::Git::Diff::BROKEN_DIFF] | |
76 | - end | |
77 | - | |
78 | 72 | # Collect array of Git::Commit objects |
79 | 73 | # between target and source branches |
80 | 74 | def unmerged_commits | ... | ... |
app/models/milestone.rb
... | ... | @@ -16,8 +16,7 @@ |
16 | 16 | class Milestone < ActiveRecord::Base |
17 | 17 | include InternalId |
18 | 18 | |
19 | - attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes | |
20 | - attr_accessor :author_id_of_changes | |
19 | + attr_accessible :title, :description, :due_date, :state_event | |
21 | 20 | |
22 | 21 | belongs_to :project |
23 | 22 | has_many :issues |
... | ... | @@ -89,6 +88,6 @@ class Milestone < ActiveRecord::Base |
89 | 88 | end |
90 | 89 | |
91 | 90 | def author_id |
92 | - author_id_of_changes | |
91 | + nil | |
93 | 92 | end |
94 | 93 | end | ... | ... |
app/models/note.rb
... | ... | @@ -199,7 +199,8 @@ class Note < ActiveRecord::Base |
199 | 199 | def downvote? |
200 | 200 | votable? && (note.start_with?('-1') || |
201 | 201 | note.start_with?(':-1:') || |
202 | - note.start_with?(':thumbsdown:') | |
202 | + note.start_with?(':thumbsdown:') || | |
203 | + note.start_with?(':thumbs_down_sign:') | |
203 | 204 | ) |
204 | 205 | end |
205 | 206 | |
... | ... | @@ -249,7 +250,8 @@ class Note < ActiveRecord::Base |
249 | 250 | def upvote? |
250 | 251 | votable? && (note.start_with?('+1') || |
251 | 252 | note.start_with?(':+1:') || |
252 | - note.start_with?(':thumbsup:') | |
253 | + note.start_with?(':thumbsup:') || | |
254 | + note.start_with?(':thumbs_up_sign:') | |
253 | 255 | ) |
254 | 256 | end |
255 | 257 | ... | ... |
app/models/project.rb
... | ... | @@ -28,6 +28,8 @@ class Project < ActiveRecord::Base |
28 | 28 | include Gitlab::VisibilityLevel |
29 | 29 | extend Enumerize |
30 | 30 | |
31 | + default_value_for :archived, false | |
32 | + | |
31 | 33 | ActsAsTaggableOn.strict_case_match = true |
32 | 34 | |
33 | 35 | attr_accessible :name, :path, :description, :issues_tracker, :label_list, |
... | ... | @@ -54,15 +56,13 @@ class Project < ActiveRecord::Base |
54 | 56 | has_one :flowdock_service, dependent: :destroy |
55 | 57 | has_one :assembla_service, dependent: :destroy |
56 | 58 | has_one :gemnasium_service, dependent: :destroy |
59 | + has_one :slack_service, dependent: :destroy | |
57 | 60 | has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" |
58 | 61 | has_one :forked_from_project, through: :forked_project_link |
59 | - | |
60 | 62 | # Merge Requests for target project should be removed with it |
61 | 63 | has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" |
62 | - | |
63 | 64 | # Merge requests from source project should be kept when source project was removed |
64 | 65 | has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest |
65 | - | |
66 | 66 | has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy |
67 | 67 | has_many :services, dependent: :destroy |
68 | 68 | has_many :events, dependent: :destroy |
... | ... | @@ -71,10 +71,8 @@ class Project < ActiveRecord::Base |
71 | 71 | has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" |
72 | 72 | has_many :hooks, dependent: :destroy, class_name: "ProjectHook" |
73 | 73 | has_many :protected_branches, dependent: :destroy |
74 | - | |
75 | 74 | has_many :users_projects, dependent: :destroy |
76 | 75 | has_many :users, through: :users_projects |
77 | - | |
78 | 76 | has_many :deploy_keys_projects, dependent: :destroy |
79 | 77 | has_many :deploy_keys, through: :deploy_keys_projects |
80 | 78 | |
... | ... | @@ -94,15 +92,12 @@ class Project < ActiveRecord::Base |
94 | 92 | validates :issues_enabled, :wall_enabled, :merge_requests_enabled, |
95 | 93 | :wiki_enabled, inclusion: { in: [true, false] } |
96 | 94 | validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true |
97 | - | |
98 | 95 | validates :namespace, presence: true |
99 | 96 | validates_uniqueness_of :name, scope: :namespace_id |
100 | 97 | validates_uniqueness_of :path, scope: :namespace_id |
101 | - | |
102 | 98 | validates :import_url, |
103 | 99 | format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" }, |
104 | 100 | if: :import? |
105 | - | |
106 | 101 | validate :check_limit, on: :create |
107 | 102 | |
108 | 103 | # Scopes |
... | ... | @@ -115,14 +110,36 @@ class Project < ActiveRecord::Base |
115 | 110 | scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } |
116 | 111 | scope :personal, ->(user) { where(namespace_id: user.namespace_id) } |
117 | 112 | scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } |
118 | - | |
119 | 113 | scope :public_only, -> { where(visibility_level: Project::PUBLIC) } |
120 | 114 | scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } |
121 | - | |
122 | 115 | scope :non_archived, -> { where(archived: false) } |
123 | 116 | |
124 | 117 | enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab |
125 | 118 | |
119 | + state_machine :import_status, initial: :none do | |
120 | + event :import_start do | |
121 | + transition :none => :started | |
122 | + end | |
123 | + | |
124 | + event :import_finish do | |
125 | + transition :started => :finished | |
126 | + end | |
127 | + | |
128 | + event :import_fail do | |
129 | + transition :started => :failed | |
130 | + end | |
131 | + | |
132 | + event :import_retry do | |
133 | + transition :failed => :started | |
134 | + end | |
135 | + | |
136 | + state :started | |
137 | + state :finished | |
138 | + state :failed | |
139 | + | |
140 | + after_transition any => :started, :do => :add_import_job | |
141 | + end | |
142 | + | |
126 | 143 | class << self |
127 | 144 | def public_and_internal_levels |
128 | 145 | [Project::PUBLIC, Project::INTERNAL] |
... | ... | @@ -161,15 +178,13 @@ class Project < ActiveRecord::Base |
161 | 178 | end |
162 | 179 | |
163 | 180 | def find_with_namespace(id) |
164 | - if id.include?("/") | |
165 | - id = id.split("/") | |
166 | - namespace = Namespace.find_by(path: id.first) | |
167 | - return nil unless namespace | |
168 | - | |
169 | - where(namespace_id: namespace.id).find_by(path: id.second) | |
170 | - else | |
171 | - where(path: id, namespace_id: nil).last | |
172 | - end | |
181 | + return nil unless id.include?("/") | |
182 | + | |
183 | + id = id.split("/") | |
184 | + namespace = Namespace.find_by(path: id.first) | |
185 | + return nil unless namespace | |
186 | + | |
187 | + where(namespace_id: namespace.id).find_by(path: id.second) | |
173 | 188 | end |
174 | 189 | |
175 | 190 | def visibility_levels |
... | ... | @@ -199,12 +214,28 @@ class Project < ActiveRecord::Base |
199 | 214 | id && persisted? |
200 | 215 | end |
201 | 216 | |
217 | + def add_import_job | |
218 | + RepositoryImportWorker.perform_in(2.seconds, id) | |
219 | + end | |
220 | + | |
202 | 221 | def import? |
203 | 222 | import_url.present? |
204 | 223 | end |
205 | 224 | |
206 | 225 | def imported? |
207 | - imported | |
226 | + import_finished? | |
227 | + end | |
228 | + | |
229 | + def import_in_progress? | |
230 | + import? && import_status == 'started' | |
231 | + end | |
232 | + | |
233 | + def import_failed? | |
234 | + import_status == 'failed' | |
235 | + end | |
236 | + | |
237 | + def import_finished? | |
238 | + import_status == 'finished' | |
208 | 239 | end |
209 | 240 | |
210 | 241 | def check_limit |
... | ... | @@ -274,7 +305,7 @@ class Project < ActiveRecord::Base |
274 | 305 | end |
275 | 306 | |
276 | 307 | def available_services_names |
277 | - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium) | |
308 | + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack) | |
278 | 309 | end |
279 | 310 | |
280 | 311 | def gitlab_ci? |
... | ... | @@ -358,18 +389,17 @@ class Project < ActiveRecord::Base |
358 | 389 | branch_name = ref.gsub("refs/heads/", "") |
359 | 390 | c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) |
360 | 391 | |
392 | + # Close merge requests | |
393 | + mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a | |
394 | + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | |
395 | + mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } | |
396 | + | |
361 | 397 | # Update code for merge requests into project between project branches |
362 | 398 | mrs = self.merge_requests.opened.by_branch(branch_name).to_a |
363 | 399 | # Update code for merge requests between project and project fork |
364 | 400 | mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a |
365 | - | |
366 | 401 | mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } |
367 | 402 | |
368 | - # Close merge requests | |
369 | - mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a | |
370 | - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | |
371 | - mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } | |
372 | - | |
373 | 403 | true |
374 | 404 | end |
375 | 405 | ... | ... |
app/models/project_hook.rb
... | ... | @@ -17,9 +17,10 @@ |
17 | 17 | class ProjectHook < WebHook |
18 | 18 | belongs_to :project |
19 | 19 | |
20 | - attr_accessible :push_events, :issues_events, :merge_requests_events | |
20 | + attr_accessible :push_events, :issues_events, :merge_requests_events, :tag_push_events | |
21 | 21 | |
22 | 22 | scope :push_hooks, -> { where(push_events: true) } |
23 | + scope :tag_push_hooks, -> { where(tag_push_events: true) } | |
23 | 24 | scope :issue_hooks, -> { where(issues_events: true) } |
24 | 25 | scope :merge_request_hooks, -> { where(merge_requests_events: true) } |
25 | 26 | end | ... | ... |
app/models/project_services/gitlab_ci_service.rb
... | ... | @@ -37,7 +37,7 @@ class GitlabCiService < Service |
37 | 37 | end |
38 | 38 | |
39 | 39 | def commit_status sha |
40 | - response = HTTParty.get(commit_status_path(sha)) | |
40 | + response = HTTParty.get(commit_status_path(sha), verify: false) | |
41 | 41 | |
42 | 42 | if response.code == 200 and response["status"] |
43 | 43 | response["status"] | ... | ... |
... | ... | @@ -0,0 +1,95 @@ |
1 | +require 'slack-notifier' | |
2 | + | |
3 | +class SlackMessage | |
4 | + def initialize(params) | |
5 | + @after = params.fetch(:after) | |
6 | + @before = params.fetch(:before) | |
7 | + @commits = params.fetch(:commits, []) | |
8 | + @project_name = params.fetch(:project_name) | |
9 | + @project_url = params.fetch(:project_url) | |
10 | + @ref = params.fetch(:ref).gsub('refs/heads/', '') | |
11 | + @username = params.fetch(:user_name) | |
12 | + end | |
13 | + | |
14 | + def compose | |
15 | + format(message) | |
16 | + end | |
17 | + | |
18 | + private | |
19 | + | |
20 | + attr_reader :after | |
21 | + attr_reader :before | |
22 | + attr_reader :commits | |
23 | + attr_reader :project_name | |
24 | + attr_reader :project_url | |
25 | + attr_reader :ref | |
26 | + attr_reader :username | |
27 | + | |
28 | + def message | |
29 | + if new_branch? | |
30 | + new_branch_message | |
31 | + elsif removed_branch? | |
32 | + removed_branch_message | |
33 | + else | |
34 | + push_message << commit_messages | |
35 | + end | |
36 | + end | |
37 | + | |
38 | + def format(string) | |
39 | + Slack::Notifier::LinkFormatter.format(string) | |
40 | + end | |
41 | + | |
42 | + def new_branch_message | |
43 | + "#{username} pushed new branch #{branch_link} to #{project_link}" | |
44 | + end | |
45 | + | |
46 | + def removed_branch_message | |
47 | + "#{username} removed branch #{ref} from #{project_link}" | |
48 | + end | |
49 | + | |
50 | + def push_message | |
51 | + "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})" | |
52 | + end | |
53 | + | |
54 | + def commit_messages | |
55 | + commits.each_with_object('') do |commit, str| | |
56 | + str << compose_commit_message(commit) | |
57 | + end | |
58 | + end | |
59 | + | |
60 | + def compose_commit_message(commit) | |
61 | + id = commit.fetch(:id)[0..5] | |
62 | + message = commit.fetch(:message) | |
63 | + url = commit.fetch(:url) | |
64 | + | |
65 | + "\n - #{message} ([#{id}](#{url}))" | |
66 | + end | |
67 | + | |
68 | + def new_branch? | |
69 | + before =~ /000000/ | |
70 | + end | |
71 | + | |
72 | + def removed_branch? | |
73 | + after =~ /000000/ | |
74 | + end | |
75 | + | |
76 | + def branch_url | |
77 | + "#{project_url}/commits/#{ref}" | |
78 | + end | |
79 | + | |
80 | + def compare_url | |
81 | + "#{project_url}/compare/#{before}...#{after}" | |
82 | + end | |
83 | + | |
84 | + def branch_link | |
85 | + "[#{ref}](#{branch_url})" | |
86 | + end | |
87 | + | |
88 | + def project_link | |
89 | + "[#{project_name}](#{project_url})" | |
90 | + end | |
91 | + | |
92 | + def compare_link | |
93 | + "[Compare changes](#{compare_url})" | |
94 | + end | |
95 | +end | ... | ... |
... | ... | @@ -0,0 +1,68 @@ |
1 | +# == Schema Information | |
2 | +# | |
3 | +# Table name: services | |
4 | +# | |
5 | +# id :integer not null, primary key | |
6 | +# type :string(255) | |
7 | +# title :string(255) | |
8 | +# token :string(255) | |
9 | +# project_id :integer not null | |
10 | +# created_at :datetime not null | |
11 | +# updated_at :datetime not null | |
12 | +# active :boolean default(FALSE), not null | |
13 | +# project_url :string(255) | |
14 | +# subdomain :string(255) | |
15 | +# room :string(255) | |
16 | +# api_key :string(255) | |
17 | +# | |
18 | + | |
19 | +class SlackService < Service | |
20 | + attr_accessible :room | |
21 | + attr_accessible :subdomain | |
22 | + | |
23 | + validates :room, presence: true, if: :activated? | |
24 | + validates :subdomain, presence: true, if: :activated? | |
25 | + validates :token, presence: true, if: :activated? | |
26 | + | |
27 | + def title | |
28 | + 'Slack' | |
29 | + end | |
30 | + | |
31 | + def description | |
32 | + 'A team communication tool for the 21st century' | |
33 | + end | |
34 | + | |
35 | + def to_param | |
36 | + 'slack' | |
37 | + end | |
38 | + | |
39 | + def fields | |
40 | + [ | |
41 | + { type: 'text', name: 'subdomain', placeholder: '' }, | |
42 | + { type: 'text', name: 'token', placeholder: '' }, | |
43 | + { type: 'text', name: 'room', placeholder: 'Ex. #general' }, | |
44 | + ] | |
45 | + end | |
46 | + | |
47 | + def execute(push_data) | |
48 | + message = SlackMessage.new(push_data.merge( | |
49 | + project_url: project_url, | |
50 | + project_name: project_name | |
51 | + )) | |
52 | + | |
53 | + notifier = Slack::Notifier.new(subdomain, token) | |
54 | + notifier.channel = room | |
55 | + notifier.username = 'GitLab' | |
56 | + notifier.ping(message.compose) | |
57 | + end | |
58 | + | |
59 | + private | |
60 | + | |
61 | + def project_name | |
62 | + project.name_with_namespace.gsub(/\s/, '') | |
63 | + end | |
64 | + | |
65 | + def project_url | |
66 | + project.web_url | |
67 | + end | |
68 | +end | ... | ... |
app/models/repository.rb
... | ... | @@ -134,6 +134,7 @@ class Repository |
134 | 134 | Rails.cache.delete(cache_key(:commit_count)) |
135 | 135 | Rails.cache.delete(cache_key(:graph_log)) |
136 | 136 | Rails.cache.delete(cache_key(:readme)) |
137 | + Rails.cache.delete(cache_key(:contribution_guide)) | |
137 | 138 | end |
138 | 139 | |
139 | 140 | def graph_log |
... | ... | @@ -167,6 +168,12 @@ class Repository |
167 | 168 | end |
168 | 169 | end |
169 | 170 | |
171 | + def contribution_guide | |
172 | + Rails.cache.fetch(cache_key(:contribution_guide)) do | |
173 | + tree(:head).contribution_guide | |
174 | + end | |
175 | + end | |
176 | + | |
170 | 177 | def head_commit |
171 | 178 | commit(self.root_ref) |
172 | 179 | end | ... | ... |
app/models/service.rb
... | ... | @@ -19,6 +19,8 @@ |
19 | 19 | # To add new service you should build a class inherited from Service |
20 | 20 | # and implement a set of methods |
21 | 21 | class Service < ActiveRecord::Base |
22 | + default_value_for :active, false | |
23 | + | |
22 | 24 | attr_accessible :title, :token, :type, :active, :api_key |
23 | 25 | |
24 | 26 | belongs_to :project | ... | ... |
app/models/snippet.rb
... | ... | @@ -20,6 +20,8 @@ class Snippet < ActiveRecord::Base |
20 | 20 | |
21 | 21 | attr_accessible :title, :content, :file_name, :expires_at, :private |
22 | 22 | |
23 | + default_value_for :private, true | |
24 | + | |
23 | 25 | belongs_to :author, class_name: "User" |
24 | 26 | |
25 | 27 | has_many :notes, as: :noteable, dependent: :destroy | ... | ... |
app/models/tree.rb
1 | 1 | class Tree |
2 | - attr_accessor :entries, :readme | |
2 | + attr_accessor :entries, :readme, :contribution_guide | |
3 | 3 | |
4 | 4 | def initialize(repository, sha, path = '/') |
5 | 5 | path = '/' if path.blank? |
... | ... | @@ -10,6 +10,11 @@ class Tree |
10 | 10 | readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name) |
11 | 11 | @readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path) |
12 | 12 | end |
13 | + | |
14 | + if contribution_tree = @entries.find(&:contributing?) | |
15 | + contribution_path = path == '/' ? contribution_tree.name : File.join(path, contribution_tree.name) | |
16 | + @contribution_guide = Gitlab::Git::Blob.find(git_repo, sha, contribution_path) | |
17 | + end | |
13 | 18 | end |
14 | 19 | |
15 | 20 | def trees | ... | ... |
app/models/user.rb
... | ... | @@ -185,7 +185,7 @@ class User < ActiveRecord::Base |
185 | 185 | where(conditions).first |
186 | 186 | end |
187 | 187 | end |
188 | - | |
188 | + | |
189 | 189 | def find_for_commit(email, name) |
190 | 190 | # Prefer email match over name match |
191 | 191 | User.where(email: email).first || |
... | ... | @@ -249,7 +249,7 @@ class User < ActiveRecord::Base |
249 | 249 | def namespace_uniq |
250 | 250 | namespace_name = self.username |
251 | 251 | if Namespace.find_by(path: namespace_name) |
252 | - self.errors.add :username, "already exist" | |
252 | + self.errors.add :username, "already exists" | |
253 | 253 | end |
254 | 254 | end |
255 | 255 | |
... | ... | @@ -275,7 +275,9 @@ class User < ActiveRecord::Base |
275 | 275 | # Projects user has access to |
276 | 276 | def authorized_projects |
277 | 277 | @authorized_projects ||= begin |
278 | - project_ids = (personal_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq | |
278 | + project_ids = personal_projects.pluck(:id) | |
279 | + project_ids += groups_projects.pluck(:id) | |
280 | + project_ids += projects.pluck(:id).uniq | |
279 | 281 | Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC') |
280 | 282 | end |
281 | 283 | end |
... | ... | @@ -406,6 +408,14 @@ class User < ActiveRecord::Base |
406 | 408 | end |
407 | 409 | end |
408 | 410 | |
411 | + def requires_ldap_check? | |
412 | + if ldap_user? | |
413 | + !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now | |
414 | + else | |
415 | + false | |
416 | + end | |
417 | + end | |
418 | + | |
409 | 419 | def solo_owned_groups |
410 | 420 | @solo_owned_groups ||= owned_groups.select do |group| |
411 | 421 | group.owners == [self] | ... | ... |
app/models/web_hook.rb
... | ... | @@ -17,6 +17,10 @@ |
17 | 17 | class WebHook < ActiveRecord::Base |
18 | 18 | include HTTParty |
19 | 19 | |
20 | + default_value_for :push_events, true | |
21 | + default_value_for :issues_events, false | |
22 | + default_value_for :merge_requests_events, false | |
23 | + | |
20 | 24 | attr_accessible :url |
21 | 25 | |
22 | 26 | # HTTParty timeout | ... | ... |
app/observers/activity_observer.rb
... | ... | @@ -1,39 +0,0 @@ |
1 | -class ActivityObserver < BaseObserver | |
2 | - observe :issue, :note, :milestone | |
3 | - | |
4 | - def after_create(record) | |
5 | - event_author_id = record.author_id | |
6 | - | |
7 | - if record.kind_of?(Note) | |
8 | - # Skip system notes, like status changes and cross-references. | |
9 | - return true if record.system? | |
10 | - | |
11 | - # Skip wall notes to prevent spamming of dashboard | |
12 | - return true if record.noteable_type.blank? | |
13 | - end | |
14 | - | |
15 | - if event_author_id | |
16 | - create_event(record, Event.determine_action(record)) | |
17 | - end | |
18 | - end | |
19 | - | |
20 | - def after_close(record, transition) | |
21 | - create_event(record, Event::CLOSED) | |
22 | - end | |
23 | - | |
24 | - def after_reopen(record, transition) | |
25 | - create_event(record, Event::REOPENED) | |
26 | - end | |
27 | - | |
28 | - protected | |
29 | - | |
30 | - def create_event(record, status) | |
31 | - Event.create( | |
32 | - project: record.project, | |
33 | - target_id: record.id, | |
34 | - target_type: record.class.name, | |
35 | - action: status, | |
36 | - author_id: current_user.id | |
37 | - ) | |
38 | - end | |
39 | -end |
app/observers/base_observer.rb
app/observers/issue_observer.rb
1 | 1 | class IssueObserver < BaseObserver |
2 | 2 | def after_create(issue) |
3 | 3 | notification.new_issue(issue, current_user) |
4 | + event_service.open_issue(issue, current_user) | |
4 | 5 | issue.create_cross_references!(issue.project, current_user) |
5 | 6 | execute_hooks(issue) |
6 | 7 | end |
7 | 8 | |
8 | 9 | def after_close(issue, transition) |
9 | 10 | notification.close_issue(issue, current_user) |
11 | + event_service.close_issue(issue, current_user) | |
10 | 12 | create_note(issue) |
11 | 13 | execute_hooks(issue) |
12 | 14 | end |
13 | 15 | |
14 | 16 | def after_reopen(issue, transition) |
17 | + event_service.reopen_issue(issue, current_user) | |
15 | 18 | create_note(issue) |
16 | 19 | execute_hooks(issue) |
17 | 20 | end | ... | ... |
app/observers/merge_request_observer.rb
1 | -class MergeRequestObserver < ActivityObserver | |
2 | - observe :merge_request | |
3 | - | |
1 | +class MergeRequestObserver < BaseObserver | |
4 | 2 | def after_create(merge_request) |
5 | - if merge_request.author_id | |
6 | - create_event(merge_request, Event.determine_action(merge_request)) | |
7 | - end | |
8 | - | |
3 | + event_service.open_mr(merge_request, current_user) | |
9 | 4 | notification.new_merge_request(merge_request, current_user) |
10 | 5 | merge_request.create_cross_references!(merge_request.project, current_user) |
11 | 6 | execute_hooks(merge_request) |
12 | 7 | end |
13 | 8 | |
14 | 9 | def after_close(merge_request, transition) |
15 | - create_event(merge_request, Event::CLOSED) | |
10 | + event_service.close_mr(merge_request, current_user) | |
16 | 11 | notification.close_mr(merge_request, current_user) |
17 | 12 | create_note(merge_request) |
18 | 13 | execute_hooks(merge_request) |
19 | 14 | end |
20 | 15 | |
21 | 16 | def after_reopen(merge_request, transition) |
22 | - create_event(merge_request, Event::REOPENED) | |
17 | + event_service.reopen_mr(merge_request, current_user) | |
23 | 18 | create_note(merge_request) |
24 | 19 | execute_hooks(merge_request) |
20 | + merge_request.reload_code | |
21 | + merge_request.mark_as_unchecked | |
25 | 22 | end |
26 | 23 | |
27 | 24 | def after_update(merge_request) |
... | ... | @@ -31,16 +28,6 @@ class MergeRequestObserver < ActivityObserver |
31 | 28 | execute_hooks(merge_request) |
32 | 29 | end |
33 | 30 | |
34 | - def create_event(record, status) | |
35 | - Event.create( | |
36 | - project: record.target_project, | |
37 | - target_id: record.id, | |
38 | - target_type: record.class.name, | |
39 | - action: status, | |
40 | - author_id: current_user.id | |
41 | - ) | |
42 | - end | |
43 | - | |
44 | 31 | private |
45 | 32 | |
46 | 33 | # Create merge request note with service comment like 'Status changed to closed' | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +class MilestoneObserver < BaseObserver | |
2 | + def after_create(milestone) | |
3 | + event_service.open_milestone(milestone, current_user) | |
4 | + end | |
5 | + | |
6 | + def after_close(milestone, transition) | |
7 | + event_service.close_milestone(milestone, current_user) | |
8 | + end | |
9 | + | |
10 | + def after_reopen(milestone, transition) | |
11 | + event_service.reopen_milestone(milestone, current_user) | |
12 | + end | |
13 | +end | ... | ... |
app/observers/note_observer.rb
... | ... | @@ -2,6 +2,12 @@ class NoteObserver < BaseObserver |
2 | 2 | def after_create(note) |
3 | 3 | notification.new_note(note) |
4 | 4 | |
5 | + # Skip system notes, like status changes and cross-references. | |
6 | + # Skip wall notes to prevent spamming of dashboard | |
7 | + if note.noteable_type.present? && !note.system | |
8 | + event_service.leave_note(note, current_user) | |
9 | + end | |
10 | + | |
5 | 11 | unless note.system? |
6 | 12 | # Create a cross-reference note if this Note contains GFM that names an |
7 | 13 | # issue, merge request, or commit. | ... | ... |
app/observers/project_observer.rb
1 | 1 | class ProjectObserver < BaseObserver |
2 | 2 | def after_create(project) |
3 | - project.update_column(:last_activity_at, project.created_at) | |
4 | - | |
5 | - return true if project.forked? | |
6 | - | |
7 | - if project.import? | |
8 | - RepositoryImportWorker.perform_in(5.seconds, project.id) | |
9 | - else | |
10 | - GitlabShellWorker.perform_async( | |
11 | - :add_repository, | |
12 | - project.path_with_namespace | |
13 | - ) | |
14 | - | |
15 | - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") | |
16 | - end | |
17 | - | |
18 | - if project.wiki_enabled? | |
19 | - begin | |
20 | - # force the creation of a wiki, | |
21 | - GollumWiki.new(project, project.owner).wiki | |
22 | - rescue GollumWiki::CouldNotCreateWikiError => ex | |
23 | - # Prevent project observer crash | |
24 | - # if failed to create wiki | |
25 | - nil | |
26 | - end | |
27 | - end | |
3 | + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") | |
28 | 4 | end |
29 | 5 | |
30 | 6 | def after_update(project) | ... | ... |
... | ... | @@ -0,0 +1,64 @@ |
1 | +# EventCreateService class | |
2 | +# | |
3 | +# Used for creating events feed on dashboard after certain user action | |
4 | +# | |
5 | +# Ex. | |
6 | +# EventCreateService.new.new_issue(issue, current_user) | |
7 | +# | |
8 | +class EventCreateService | |
9 | + def open_issue(issue, current_user) | |
10 | + create_event(issue, current_user, Event::CREATED) | |
11 | + end | |
12 | + | |
13 | + def close_issue(issue, current_user) | |
14 | + create_event(issue, current_user, Event::CLOSED) | |
15 | + end | |
16 | + | |
17 | + def reopen_issue(issue, current_user) | |
18 | + create_event(issue, current_user, Event::REOPENED) | |
19 | + end | |
20 | + | |
21 | + def open_mr(merge_request, current_user) | |
22 | + create_event(merge_request, current_user, Event::CREATED) | |
23 | + end | |
24 | + | |
25 | + def close_mr(merge_request, current_user) | |
26 | + create_event(merge_request, current_user, Event::CLOSED) | |
27 | + end | |
28 | + | |
29 | + def reopen_mr(merge_request, current_user) | |
30 | + create_event(merge_request, current_user, Event::REOPENED) | |
31 | + end | |
32 | + | |
33 | + def merge_mr(merge_request, current_user) | |
34 | + create_event(merge_request, current_user, Event::MERGED) | |
35 | + end | |
36 | + | |
37 | + def open_milestone(milestone, current_user) | |
38 | + create_event(milestone, current_user, Event::CREATED) | |
39 | + end | |
40 | + | |
41 | + def close_milestone(milestone, current_user) | |
42 | + create_event(milestone, current_user, Event::CLOSED) | |
43 | + end | |
44 | + | |
45 | + def reopen_milestone(milestone, current_user) | |
46 | + create_event(milestone, current_user, Event::REOPENED) | |
47 | + end | |
48 | + | |
49 | + def leave_note(note, current_user) | |
50 | + create_event(note, current_user, Event::COMMENTED) | |
51 | + end | |
52 | + | |
53 | + private | |
54 | + | |
55 | + def create_event(record, current_user, status) | |
56 | + Event.create( | |
57 | + project: record.project, | |
58 | + target_id: record.id, | |
59 | + target_type: record.class.name, | |
60 | + action: status, | |
61 | + author_id: current_user.id | |
62 | + ) | |
63 | + end | |
64 | +end | ... | ... |