Commit ea82bcd396c9e6ab8542474ee70365297a4ff2dc

Authored by Timm Drevensek
2 parents c0744e5b 24e9c5e8

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
... ... @@ -35,3 +35,4 @@ doc/code/*
35 35 *.log
36 36 public/uploads.*
37 37 public/assets/
  38 +.envrc
... ...
.rspec
1   ---color --drb
  1 +--color
... ...
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&#39;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
1   -web: bundle exec unicorn_rails -p $PORT -E development
  1 +web: bundle exec unicorn_rails -p $PORT -E development -c config/unicorn_development.rb
2 2 worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
... ...
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   -6.7.0.pre
  1 +6.8.0.pre
... ...
app/assets/images/authbuttons/github_32.png 0 → 100644

1.86 KB

app/assets/images/authbuttons/github_64.png 0 → 100644

4.34 KB

app/assets/images/authbuttons/google_32.png 0 → 100644

1.57 KB

app/assets/images/authbuttons/google_64.png 0 → 100644

3.36 KB

app/assets/images/authbuttons/twitter_32.png 0 → 100644

1.38 KB

app/assets/images/authbuttons/twitter_64.png 0 → 100644

3.25 KB

app/assets/images/bg_fallback.png 0 → 100644

2.91 KB

app/assets/images/icon_sprite.png 0 → 100644

2.72 KB

app/assets/images/progress_bar.gif 0 → 100644

494 Bytes

app/assets/images/slider_handles.png 0 → 100644

4.03 KB

app/assets/images/ui-icons_222222_256x240.png 0 → 100644

4.09 KB

app/assets/images/ui-icons_454545_256x240.png 0 → 100644

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
app/assets/javascripts/application.js.coffee 0 → 100644
... ... @@ -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
1 1 input[type='search'].search-text-input {
2   - background-image: url("icon-search.png");
  2 + background-image: image-url("icon-search.png");
3 3 background-repeat: no-repeat;
4 4 background-position: 10px;
5 5 padding-left: 25px;
... ...
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
... ... @@ -178,3 +178,9 @@
178 178 .shadow {
179 179 @include box-shadow(0 5px 15px #000);
180 180 }
  181 +
  182 +.wiki, .note-body {
  183 + .highlight {
  184 + border: 1px solid #DDD;
  185 + }
  186 +}
... ...
app/assets/stylesheets/main/layout.scss
... ... @@ -5,9 +5,7 @@ html {
5 5 }
6 6  
7 7 body {
8   - -webkit-font-smoothing: antialiased;
9   - -moz-osx-font-smoothing: grayscale;
10   - margin-bottom: 20px;
  8 + padding-bottom: 20px;
11 9 }
12 10  
13 11 .container {
... ...
app/assets/stylesheets/main/mixins.scss
... ... @@ -84,6 +84,9 @@
84 84 }
85 85  
86 86 @mixin md-typography {
  87 + font-size: 14px;
  88 + line-height: 1.6;
  89 +
87 90 img {
88 91 max-width: 100%;
89 92 }
... ...
app/assets/stylesheets/print.scss 0 → 100644
... ... @@ -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
... ... @@ -61,7 +61,7 @@
61 61 }
62 62  
63 63 .project-row, .group-row {
64   - padding: 10px 12px !important;
  64 + padding: 8px 12px !important;
65 65 font-size: 14px;
66 66 line-height: 24px;
67 67  
... ...
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
... ... @@ -89,3 +89,9 @@
89 89 .merge-request-form-info {
90 90 padding-top: 15px;
91 91 }
  92 +
  93 +// hide mr close link for inline diff comment form
  94 +.diff-file .close-mr-link,
  95 +.diff-file .reopen-mr-link {
  96 + display: none;
  97 +}
... ...
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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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
... ...
app/controllers/passwords_controller.rb 0 → 100644
... ... @@ -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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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
... ... @@ -10,7 +10,7 @@ module ProfileHelper
10 10 end
11 11  
12 12 def show_profile_social_tab?
13   - Gitlab.config.omniauth.enabled && !current_user.ldap_user?
  13 + enabled_social_providers.any? && !current_user.ldap_user?
14 14 end
15 15  
16 16 def show_profile_remove_tab?
... ...
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 + "&hellip;".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   - "&hellip;".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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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"]
... ...
app/models/project_services/slack_message.rb 0 → 100644
... ... @@ -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
... ...
app/models/project_services/slack_service.rb 0 → 100644
... ... @@ -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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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
... ... @@ -3,6 +3,10 @@ class BaseObserver &lt; ActiveRecord::Observer
3 3 NotificationService.new
4 4 end
5 5  
  6 + def event_service
  7 + EventCreateService.new
  8 + end
  9 +
6 10 def log_info message
7 11 Gitlab::AppLogger.info message
8 12 end
... ...
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 &lt; 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'
... ...
app/observers/milestone_observer.rb 0 → 100644
... ... @@ -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 &lt; 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)
... ...
app/services/event_create_service.rb 0 → 100644
... ... @@ -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
... ...