Commit 2955ca61 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into reference-pipeline-and-caching

parents e1e67d38 09e712c0
...@@ -87,3 +87,12 @@ flay: ...@@ -87,3 +87,12 @@ flay:
tags: tags:
- ruby - ruby
- mysql - mysql
bundler:audit:
script:
- "bundle exec bundle-audit update"
- "bundle exec bundle-audit check"
tags:
- ruby
- mysql
allow_failure: true
...@@ -888,7 +888,7 @@ Lint/RequireParentheses: ...@@ -888,7 +888,7 @@ Lint/RequireParentheses:
Lint/RescueException: Lint/RescueException:
Description: 'Avoid rescuing the Exception class.' Description: 'Avoid rescuing the Exception class.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
Enabled: false Enabled: true
Lint/ShadowingOuterLocalVariable: Lint/ShadowingOuterLocalVariable:
Description: >- Description: >-
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.3.0 (unreleased) v 8.3.0 (unreleased)
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
- Fix 500 error when update group member permission
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
- Add ignore whitespace change option to commit view
- Fire update hook from GitLab
v 8.2.2
- Fix 404 in redirection after removing a project (Stan Hu)
- Ensure cached application settings are refreshed at startup (Stan Hu)
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- Fix: Raw private snippets access workflow
- Prevent "413 Request entity too large" errors when pushing large files with LFS
v 8.2.1
- Forcefully update builds that didn't want to update with state machine
- Fix: saving GitLabCiService as Admin Template
v 8.2.0 v 8.2.0
- Improved performance of finding projects and groups in various places - Improved performance of finding projects and groups in various places
- Improved performance of rendering user profile pages and Atom feeds - Improved performance of rendering user profile pages and Atom feeds
- Expose build artifacts path as config option
- Fix grouping of contributors by email in graph. - Fix grouping of contributors by email in graph.
- Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Improved performance of finding issues with/without labels
- Fix Drone CI service template not saving properly (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu)
- Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu)
- Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
...@@ -15,6 +32,7 @@ v 8.2.0 ...@@ -15,6 +32,7 @@ v 8.2.0
- Add allow_failure field to commit status API (Stan Hu) - Add allow_failure field to commit status API (Stan Hu)
- Commits without .gitlab-ci.yml are marked as skipped - Commits without .gitlab-ci.yml are marked as skipped
- Save detailed error when YAML syntax is invalid - Save detailed error when YAML syntax is invalid
- Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml
- Added build artifacts - Added build artifacts
- Improved performance of replacing references in comments - Improved performance of replacing references in comments
- Show last project commit to default branch on project home page - Show last project commit to default branch on project home page
...@@ -33,6 +51,7 @@ v 8.2.0 ...@@ -33,6 +51,7 @@ v 8.2.0
- Allow to define cache in `.gitlab-ci.yml` - Allow to define cache in `.gitlab-ci.yml`
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
- Remove deprecated CI events from project settings page - Remove deprecated CI events from project settings page
- Improve personal snippet access workflow (Douglas Alexandre)
- [API] Add ability to fetch the commit ID of the last commit that actually touched a file - [API] Add ability to fetch the commit ID of the last commit that actually touched a file
- Fix omniauth documentation setting for omnibus configuration (Jon Cairns) - Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
- Add "New file" link to dropdown on project page - Add "New file" link to dropdown on project page
...@@ -54,7 +73,9 @@ v 8.2.0 ...@@ -54,7 +73,9 @@ v 8.2.0
- Fix trailing whitespace issue in merge request/issue title - Fix trailing whitespace issue in merge request/issue title
- Fix bug when milestone/label filter was empty for dashboard issues page - Fix bug when milestone/label filter was empty for dashboard issues page
- Add ability to create milestone in group projects from single form - Add ability to create milestone in group projects from single form
- Add option to create merge request when editing/creating a file (Dirceu Tiegs)
- Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez) - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
- Add Award Emoji to issue and merge request pages
v 8.1.4 v 8.1.4
- Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
......
...@@ -23,10 +23,20 @@ Issues and merge requests should be in English and contain appropriate language ...@@ -23,10 +23,20 @@ Issues and merge requests should be in English and contain appropriate language
## Helping others ## Helping others
Please help other GitLab users when you can. Please help other GitLab users when you can.
The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/).
Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel. Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel.
You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day.
## I want to contribute!
If you want to contribute to GitLab, but are not sure where to start,
look for [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=up-for-grabs)
with the label `up-for-grabs`.
These issues will be of reasonable size and challenge, for anyone to start
contributing to GitLab.
This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4).
## Issue tracker ## Issue tracker
To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/). To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/).
...@@ -47,10 +57,10 @@ Please send a merge request with a tested solution or a merge request with a fai ...@@ -47,10 +57,10 @@ Please send a merge request with a tested solution or a merge request with a fai
1. **Observed behavior** 1. **Observed behavior**
1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise. 1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
1. **Output of checks** 1. **Output of checks**
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:check SANITIZE=true`); For installations from source: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
* Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md) * Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
* Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page) * Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page)
* Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) * Describe your setup (use relevant parts from the env info: For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:env:info`; For installations from source: `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem 1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem
## Merge requests ## Merge requests
...@@ -59,7 +69,7 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and ...@@ -59,7 +69,7 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and
Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls). Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls).
If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. If you are new to GitLab development (or web development in general), search for the label `easyfix` ([GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [GitHub](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file. To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file.
...@@ -99,7 +109,7 @@ If you contribute to GitLab please know that changes involve more than just code ...@@ -99,7 +109,7 @@ If you contribute to GitLab please know that changes involve more than just code
We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html). We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html).
Please ensure you support the feature you contribute through all of these steps. Please ensure you support the feature you contribute through all of these steps.
1. Description explaning the relevancy (see following item) 1. Description explaining the relevancy (see following item)
1. Working and clean code that is commented where needed 1. Working and clean code that is commented where needed
1. Unit and integration tests that pass on the CI server 1. Unit and integration tests that pass on the CI server
1. Documented in the /doc directory 1. Documented in the /doc directory
...@@ -163,7 +173,7 @@ If you add a dependency in GitLab (such as an operating system package) please c ...@@ -163,7 +173,7 @@ If you add a dependency in GitLab (such as an operating system package) please c
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. [Database Migrations](doc/development/migration_style_guide.md) 1. [Database Migrations](doc/development/migration_style_guide.md)
1. [Documentation styleguide](doc_styleguide.md) 1. [Documentation styleguide](doc_styleguide.md)
1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). 1. Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
......
source "https://rubygems.org" source "https://rubygems.org"
gem 'rails', '4.1.12' gem 'rails', '4.2.4'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
gem 'responders', '~> 2.0'
# Specify a sprockets version due to security issue # Specify a sprockets version due to security issue
# See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY
...@@ -16,7 +20,7 @@ gem "pg", '~> 0.18.2', group: :postgres ...@@ -16,7 +20,7 @@ gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 3.5.2' gem 'devise', '~> 3.5.2'
gem 'devise-async', '~> 0.9.0' gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.1.3' gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2' gem 'omniauth', '~> 1.2.2'
gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-facebook', '~> 3.0.0'
...@@ -28,7 +32,7 @@ gem 'omniauth-saml', '~> 1.4.0' ...@@ -28,7 +32,7 @@ gem 'omniauth-saml', '~> 1.4.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd' gem 'omniauth_crowd'
gem 'rack-oauth2', '~> 1.0.5' gem 'rack-oauth2', '~> 1.2.1'
# Two-factor authentication # Two-factor authentication
gem 'devise-two-factor', '~> 2.0.0' gem 'devise-two-factor', '~> 2.0.0'
...@@ -62,9 +66,6 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' ...@@ -62,9 +66,6 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# based on human-friendly examples # based on human-friendly examples
gem "stamp", '~> 0.6.0' gem "stamp", '~> 0.6.0'
# Enumeration fields
gem 'enumerize', '~> 0.7.0'
# Pagination # Pagination
gem "kaminari", "~> 0.16.3" gem "kaminari", "~> 0.16.3"
...@@ -95,9 +96,10 @@ gem 'redcarpet', '~> 3.3.3' ...@@ -95,9 +96,10 @@ gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.2.9' gem 'RedCloth', '~> 4.2.9'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~>0.3.6' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'net-ssh', '~> 3.0.1'
# Diffs # Diffs
gem 'diffy', '~> 3.0.3' gem 'diffy', '~> 3.0.3'
...@@ -125,8 +127,7 @@ gem 'sidetiq', '~> 0.6.3' ...@@ -125,8 +127,7 @@ gem 'sidetiq', '~> 0.6.3'
gem "httparty", '~> 0.13.3' gem "httparty", '~> 0.13.3'
# Colored output to console # Colored output to console
gem "colored", '~> 1.2' gem "colorize", '~> 0.7.0'
gem "colorize", '~> 0.5.8'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
...@@ -154,7 +155,7 @@ gem "gemnasium-gitlab-service", "~> 0.2" ...@@ -154,7 +155,7 @@ gem "gemnasium-gitlab-service", "~> 0.2"
gem "slack-notifier", "~> 1.2.0" gem "slack-notifier", "~> 1.2.0"
# Asana integration # Asana integration
gem 'asana', '~> 0.0.6' gem 'asana', '~> 0.4.0'
# FogBugz integration # FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
...@@ -187,13 +188,13 @@ gem "sass-rails", '~> 4.0.5' ...@@ -187,13 +188,13 @@ gem "sass-rails", '~> 4.0.5'
gem "coffee-rails", '~> 4.1.0' gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.7.2' gem "uglifier", '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0' gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.0.1' gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.0' gem 'bootstrap-sass', '~> 3.0'
gem 'font-awesome-rails', '~> 4.2' gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.1' gem 'gitlab_emoji', '~> 0.1'
gem 'gon', '~> 5.0.0' gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 3.1.3' gem 'jquery-rails', '~> 3.1.3'
gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-scrollto-rails', '~> 1.4.3'
...@@ -214,6 +215,7 @@ group :development do ...@@ -214,6 +215,7 @@ group :development do
gem 'rerun', '~> 0.10.0' gem 'rerun', '~> 0.10.0'
gem 'bullet', require: false gem 'bullet', require: false
gem 'rblineprof', platform: :mri, require: false gem 'rblineprof', platform: :mri, require: false
gem 'web-console', '~> 2.0'
# Better errors handler # Better errors handler
gem 'better_errors', '~> 1.0.1' gem 'better_errors', '~> 1.0.1'
...@@ -261,6 +263,7 @@ group :development, :test do ...@@ -261,6 +263,7 @@ group :development, :test do
gem 'simplecov', '~> 0.10.0', require: false gem 'simplecov', '~> 0.10.0', require: false
gem 'flog', require: false gem 'flog', require: false
gem 'flay', require: false gem 'flay', require: false
gem 'bundler-audit', require: false
gem 'benchmark-ips', require: false gem 'benchmark-ips', require: false
end end
...@@ -269,7 +272,7 @@ group :test do ...@@ -269,7 +272,7 @@ group :test do
gem 'shoulda-matchers', '~> 2.8.0', require: false gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec', '~> 1.6.0' gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0' gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.2.2' gem 'test_after_commit', '~> 0.4.2'
gem 'sham_rack' gem 'sham_rack'
end end
......
This diff is collapsed.
...@@ -44,6 +44,7 @@ Workflow labels are purposely not very detailed since that would be hard to keep ...@@ -44,6 +44,7 @@ Workflow labels are purposely not very detailed since that would be hard to keep
- *UX* needs needs help from a UX designer - *UX* needs needs help from a UX designer
- *Frontend* needs help from a Front-end engineer - *Frontend* needs help from a Front-end engineer
- *Graphics* needs help from a Graphics designer - *Graphics* needs help from a Graphics designer
- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label.
......
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default
# mail_room: bundle exec mail_room -q -c config/mail_room.yml # mail_room: bundle exec mail_room -q -c config/mail_room.yml
...@@ -27,8 +27,6 @@ There are two editions of GitLab: ...@@ -27,8 +27,6 @@ There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license. - GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/). - GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code.
## Website ## Website
On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
......
app/assets/images/icon-link.png

726 Bytes | W: | H:

app/assets/images/icon-link.png

1.1 KB | W: | H:

app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
  • 2-up
  • Swipe
  • Onion skin
class @AwardsHandler
constructor: (@post_emoji_url, @noteable_type, @noteable_id) ->
addAward: (emoji) ->
@postEmoji emoji, =>
@addAwardToEmojiBar(emoji)
addAwardToEmojiBar: (emoji, custom_path = '') ->
if @exist(emoji)
if @isActive(emoji)
@decrementCounter(emoji)
else
counter = @findEmojiIcon(emoji).siblings(".counter")
counter.text(parseInt(counter.text()) + 1)
counter.parent().addClass("active")
@addMeToAuthorList(emoji)
else
@createEmoji(emoji, custom_path)
exist: (emoji) ->
@findEmojiIcon(emoji).length > 0
isActive: (emoji) ->
@findEmojiIcon(emoji).parent().hasClass("active")
decrementCounter: (emoji) ->
counter = @findEmojiIcon(emoji).siblings(".counter")
if parseInt(counter.text()) > 1
counter.text(parseInt(counter.text()) - 1)
counter.parent().removeClass("active")
@removeMeFromAuthorList(emoji)
else
award = counter.parent()
award.tooltip("destroy")
award.remove()
removeMeFromAuthorList: (emoji) ->
award_block = @findEmojiIcon(emoji).parent()
authors = award_block.attr("data-original-title").split(", ")
authors = _.without(authors, "me").join(", ")
award_block.attr("title", authors)
@resetTooltip(award_block)
addMeToAuthorList: (emoji) ->
award_block = @findEmojiIcon(emoji).parent()
authors = award_block.attr("data-original-title").split(", ")
authors.push("me")
award_block.attr("title", authors.join(", "))
@resetTooltip(award_block)
resetTooltip: (award) ->
award.tooltip("destroy")
# "destroy" call is asynchronous, this is why we need to set timeout.
setTimeout (->
award.tooltip()
), 200
createEmoji: (emoji, custom_path) ->
nodes = []
nodes.push("<div class='award active' title='me'>")
nodes.push("<div class='icon' data-emoji='" + emoji + "'>")
nodes.push(@getImage(emoji, custom_path))
nodes.push("</div>")
nodes.push("<div class='counter'>1")
nodes.push("</div></div>")
$(".awards-controls").before(nodes.join("\n"))
$(".award").tooltip()
getImage: (emoji, custom_path) ->
if custom_path
$("<img>").attr({src: custom_path, width: 20, height: 20}).wrap("<div>").parent().html()
else
$("li[data-emoji='" + emoji + "']").html()
postEmoji: (emoji, callback) ->
$.post @post_emoji_url, { note: {
note: ":" + emoji + ":"
noteable_type: @noteable_type
noteable_id: @noteable_id
}},(data) ->
if data.ok
callback.call()
findEmojiIcon: (emoji) ->
$(".icon[data-emoji='" + emoji + "']")
\ No newline at end of file
...@@ -23,18 +23,6 @@ class @BlobFileDropzone ...@@ -23,18 +23,6 @@ class @BlobFileDropzone
init: -> init: ->
this.on 'addedfile', (file) -> this.on 'addedfile', (file) ->
$('.dropzone-alerts').html('').hide() $('.dropzone-alerts').html('').hide()
commit_message = form.find('#commit_message')[0]
if /^Upload/.test(commit_message.placeholder)
commit_message.placeholder = 'Upload ' + file.name
return
this.on 'removedfile', (file) ->
commit_message = form.find('#commit_message')[0]
if /^Upload/.test(commit_message.placeholder)
commit_message.placeholder = 'Upload new file'
return return
...@@ -47,8 +35,9 @@ class @BlobFileDropzone ...@@ -47,8 +35,9 @@ class @BlobFileDropzone
return return
this.on 'sending', (file, xhr, formData) -> this.on 'sending', (file, xhr, formData) ->
formData.append('new_branch', form.find('#new_branch').val()) formData.append('new_branch', form.find('.js-new-branch').val())
formData.append('commit_message', form.find('#commit_message').val()) formData.append('create_merge_request', form.find('.js-create-merge-request').val())
formData.append('commit_message', form.find('.js-commit-message').val())
return return
# Override behavior of adding error underneath preview # Override behavior of adding error underneath preview
......
#= require clipboard #= require clipboard
$ -> genericSuccess = (e) ->
clipboard = new Clipboard '.js-clipboard-trigger', showTooltip(e.trigger, 'Copied!')
text: (trigger) ->
$target = $(trigger.nextElementSibling || trigger.previousElementSibling) # Clear the selection and blur the trigger so it loses its border
$target.data('clipboard-text') || $target.text().trim() e.clearSelection()
$(e.trigger).blur()
clipboard.on 'success', (e) -> # Safari doesn't support `execCommand`, so instead we inform the user to
$(e.trigger). # copy manually.
tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). #
tooltip('show') # See http://clipboardjs.com/#browser-support
genericError = (e) ->
if /Mac/i.test(navigator.userAgent)
key = '&#8984;' # Command
else
key = 'Ctrl'
# Clear the selection and blur the trigger so it loses its border showTooltip(e.trigger, "Press #{key}-C to copy")
e.clearSelection()
$(e.trigger).blur()
# Manually hide the tooltip after 1 second showTooltip = (target, title) ->
setTimeout(-> $(target).
$(e.trigger).tooltip('hide') tooltip(
, 1000) container: 'body'
html: 'true'
placement: 'auto bottom'
title: title
trigger: 'manual'
).
tooltip('show').
one('mouseleave', -> $(this).tooltip('hide'))
$ ->
clipboard = new Clipboard '[data-clipboard-target], [data-clipboard-text]'
clipboard.on 'success', genericSuccess
clipboard.on 'error', genericError
#= require markdown_preview
class @DropzoneInput class @DropzoneInput
constructor: (form) -> constructor: (form) ->
Dropzone.autoDiscover = false Dropzone.autoDiscover = false
...@@ -11,17 +13,14 @@ class @DropzoneInput ...@@ -11,17 +13,14 @@ class @DropzoneInput
uploadProgress = $("<div class=\"div-dropzone-progress\"></div>") uploadProgress = $("<div class=\"div-dropzone-progress\"></div>")
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>" btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_uploads_path = window.project_uploads_path or null project_uploads_path = window.project_uploads_path or null
markdown_preview_path = window.markdown_preview_path or null
max_file_size = gon.max_file_size or 10 max_file_size = gon.max_file_size or 10
form_textarea = $(form).find("textarea.markdown-area") form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>" form_textarea.wrap "<div class=\"div-dropzone\"></div>"
form_textarea.on 'paste', (event) => form_textarea.on 'paste', (event) =>
handlePaste(event) handlePaste(event)
form_textarea.on "input", ->
hideReferencedUsers() $(form).setupMarkdownPreview()
form_textarea.on "blur", ->
renderMarkdown()
form_dropzone = $(form).find('.div-dropzone') form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper" form_dropzone.parent().addClass "div-dropzone-wrapper"
...@@ -34,42 +33,6 @@ class @DropzoneInput ...@@ -34,42 +33,6 @@ class @DropzoneInput
"opacity": 0 "opacity": 0
"display": "none" "display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
renderMarkdown()
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = form_dropzone.dropzone( dropzone = form_dropzone.dropzone(
url: project_uploads_path url: project_uploads_path
dictDefaultMessage: "" dictDefaultMessage: ""
...@@ -136,41 +99,6 @@ class @DropzoneInput ...@@ -136,41 +99,6 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea") child = $(dropzone[0]).children("textarea")
hideReferencedUsers = ->
referencedUsers = form.find(".referenced-users")
referencedUsers.hide()
renderReferencedUsers = (users) ->
referencedUsers = form.find(".referenced-users")
if referencedUsers.length
if users.length >= 10
referencedUsers.show()
referencedUsers.find(".js-referenced-users-count").text users.length
else
referencedUsers.hide()
renderMarkdown = ->
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
hideReferencedUsers()
else
preview.text "Loading..."
$.ajax(
type: "POST",
url: markdown_preview_path,
data: {
text: mdText
},
dataType: "json"
).success (data) ->
preview.html data.body
preview.syntaxHighlight()
renderReferencedUsers data.references.users
formatLink = (link) -> formatLink = (link) ->
text = "[#{link.alt}](#{link.url})" text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image text = "!#{text}" if link.is_image
......
# MarkdownPreview
#
# Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
# and showing a warning when more than `x` users are referenced.
#
class @MarkdownPreview
# Minimum number of users referenced before triggering a warning
referenceThreshold: 10
showPreview: (form) ->
preview = form.find('.js-md-preview')
mdText = form.find('textarea.markdown-area').val()
if mdText.trim().length == 0
preview.text('Nothing to preview.')
@hideReferencedUsers(form)
else
preview.text('Loading...')
@renderMarkdown mdText, (response) =>
preview.html(response.body)
preview.syntaxHighlight()
@renderReferencedUsers(response.references.users, form)
renderMarkdown: (text, success) ->
return unless window.markdown_preview_path
$.ajax
type: 'POST'
url: window.markdown_preview_path
data: { text: text }
dataType: 'json'
success: success
hideReferencedUsers: (form) ->
referencedUsers = form.find('.referenced-users')
referencedUsers.hide()
renderReferencedUsers: (users, form) ->
referencedUsers = form.find('.referenced-users')
if referencedUsers.length
if users.length >= @referenceThreshold
referencedUsers.show()
referencedUsers.find('.js-referenced-users-count').text(users.length)
else
referencedUsers.hide()
markdownPreview = new MarkdownPreview()
previewButtonSelector = '.js-md-preview-button'
writeButtonSelector = '.js-md-write-button'
$.fn.setupMarkdownPreview = ->
$form = $(this)
form_textarea = $form.find('textarea.markdown-area')
form_textarea.on 'input', -> markdownPreview.hideReferencedUsers($form)
form_textarea.on 'blur', -> markdownPreview.showPreview($form)
$(document).on 'click', previewButtonSelector, (e) ->
e.preventDefault()
$form = $(this).closest('form')
# toggle tabs
$form.find(writeButtonSelector).parent().removeClass('active')
$form.find(previewButtonSelector).parent().addClass('active')
# toggle content
$form.find('.md-write-holder').hide()
$form.find('.md-preview-holder').show()
markdownPreview.showPreview($form)
$(document).on 'click', writeButtonSelector, (e) ->
e.preventDefault()
$form = $(this).closest('form')
# toggle tabs
$form.find(writeButtonSelector).parent().addClass('active')
$form.find(previewButtonSelector).parent().removeClass('active')
# toggle content
$form.find('.md-write-holder').show()
$form.find('.md-preview-holder').hide()
class @NewCommitForm
constructor: (form) ->
@newBranch = form.find('.js-new-branch')
@originalBranch = form.find('.js-original-branch')
@createMergeRequest = form.find('.js-create-merge-request')
@createMergeRequestFormGroup = form.find('.js-create-merge-request-form-group')
@renderDestination()
@newBranch.keyup @renderDestination
renderDestination: =>
different = @newBranch.val() != @originalBranch.val()
if different
@createMergeRequestFormGroup.show()
@createMergeRequest.prop('checked', true) unless @wasDifferent
else
@createMergeRequestFormGroup.hide()
@createMergeRequest.prop('checked', false)
@wasDifferent = different
...@@ -113,13 +113,16 @@ class @Notes ...@@ -113,13 +113,16 @@ class @Notes
renderNote: (note) -> renderNote: (note) ->
# render note if it not present in loaded list # render note if it not present in loaded list
# or skip if rendered # or skip if rendered
if @isNewNote(note) if @isNewNote(note) && !note.award
@note_ids.push(note.id) @note_ids.push(note.id)
$('ul.main-notes-list'). $('ul.main-notes-list').
append(note.html). append(note.html).
syntaxHighlight() syntaxHighlight()
@initTaskList() @initTaskList()
if note.award
awards_handler.addAwardToEmojiBar(note.note, note.emoji_path)
### ###
Check if note does not exists on page Check if note does not exists on page
### ###
...@@ -255,7 +258,6 @@ class @Notes ...@@ -255,7 +258,6 @@ class @Notes
### ###
addNote: (xhr, note, status) => addNote: (xhr, note, status) =>
@renderNote(note) @renderNote(note)
@updateVotes()
### ###
Called in response to the new note form being submitted Called in response to the new note form being submitted
...@@ -473,9 +475,6 @@ class @Notes ...@@ -473,9 +475,6 @@ class @Notes
form = $(e.target).closest(".js-discussion-note-form") form = $(e.target).closest(".js-discussion-note-form")
@removeDiscussionNoteForm(form) @removeDiscussionNoteForm(form)
updateVotes: ->
true
### ###
Called after an attachment file has been selected. Called after an attachment file has been selected.
......
class @Project class @Project
constructor: -> constructor: ->
# Git clone panel switcher # Git protocol switcher
cloneHolder = $('.git-clone-holder') $('.js-protocol-switch').click ->
if cloneHolder.length return if $(@).hasClass('active')
$('a, button', cloneHolder).click ->
$('a, button', cloneHolder).removeClass 'active' # Toggle 'active' for both buttons
$(@).addClass 'active' $('.js-protocol-switch').toggleClass('active')
$('#project_clone', cloneHolder).val $(@).data 'clone'
$(".clone").text("").append $(@).data 'clone' url = $(@).data('clone')
# Update the input field
$('#project_clone').val(url)
# Update the command line instructions
$('.clone').text(url)
# Ref switcher # Ref switcher
$('.project-refs-select').on 'change', -> $('.project-refs-select').on 'change', ->
...@@ -39,4 +45,4 @@ class @Project ...@@ -39,4 +45,4 @@ class @Project
when 4 then label = ' On Mention ' when 4 then label = ' On Mention '
$('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>") $('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
$(@).parents('ul').find('li.active').removeClass 'active' $(@).parents('ul').find('li.active').removeClass 'active'
$(@).parent().addClass 'active' $(@).parent().addClass 'active'
\ No newline at end of file
...@@ -58,11 +58,8 @@ class @UsersSelect ...@@ -58,11 +58,8 @@ class @UsersSelect
query.callback(data) query.callback(data)
initSelection: (element, callback) => initSelection: (args...) =>
id = $(element).val() @initSelection(args...)
if id != "" && id != "0"
@user(id, callback)
formatResult: (args...) => formatResult: (args...) =>
@formatResult(args...) @formatResult(args...)
formatSelection: (args...) => formatSelection: (args...) =>
...@@ -71,6 +68,14 @@ class @UsersSelect ...@@ -71,6 +68,14 @@ class @UsersSelect
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m m
initSelection: (element, callback) ->
id = $(element).val()
if id == "0"
nullUser = { name: 'Unassigned' }
callback(nullUser)
else if id != ""
@user(id, callback)
formatResult: (user) -> formatResult: (user) ->
if user.avatar_url if user.avatar_url
avatar = user.avatar_url avatar = user.avatar_url
......
...@@ -64,7 +64,7 @@ pre { ...@@ -64,7 +64,7 @@ pre {
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus { .dropdown-menu > li > a:focus {
background: $gl-primary; background: $gl-primary;
color: #FFF color: #FFF;
} }
.str-truncated { .str-truncated {
......
...@@ -127,3 +127,8 @@ ul.content-list { ...@@ -127,3 +127,8 @@ ul.content-list {
} }
} }
.panel > .content-list {
li {
margin: 0;
}
}
...@@ -32,3 +32,7 @@ ...@@ -32,3 +32,7 @@
} }
} }
} }
.panel > .gl-pagination {
margin: 0;
}
...@@ -190,6 +190,10 @@ ...@@ -190,6 +190,10 @@
.btn { .btn {
min-width: 124px; min-width: 124px;
} }
.btn-clipboard {
min-width: 0px;
}
} }
&.panel-small { &.panel-small {
......
...@@ -256,3 +256,9 @@ textarea.js-gfm-input { ...@@ -256,3 +256,9 @@ textarea.js-gfm-input {
.strikethrough { .strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }
h1, h2, h3, h4 {
small {
color: $gl-gray;
}
}
.new-group-member-holder {
margin-top: 50px;
padding-top: 20px;
}
.member-search-form { .member-search-form {
float: left; float: left;
} }
...@@ -15,4 +10,4 @@ ...@@ -15,4 +10,4 @@
.form-control { .form-control {
height: 42px; height: 42px;
} }
} }
\ No newline at end of file
...@@ -101,3 +101,71 @@ ...@@ -101,3 +101,71 @@
background-color: $background-color; background-color: $background-color;
} }
} }
.awards {
@include clearfix;
line-height: 34px;
margin: 2px 0;
.award {
@include border-radius(5px);
border: 1px solid;
padding: 0px 10px;
float: left;
margin: 0 5px;
border-color: $border-color;
cursor: pointer;
&.active {
border-color: $border-gray-light;
background-color: $gray-light;
.counter {
font-weight: bold;
}
}
.icon {
float: left;
margin-right: 10px;
}
.counter {
float: left;
}
}
.awards-controls {
margin-left: 10px;
float: left;
.add-award {
font-size: 24px;
color: $gl-gray;
position: relative;
top: 2px;
&:hover,
&:link {
text-decoration: none;
}
}
.awards-menu {
padding: $gl-padding;
min-width: 214px;
> li {
margin: 5px;
}
}
}
.awards-menu{
li {
float: left;
margin: 3px;
}
}
}
...@@ -90,7 +90,12 @@ ...@@ -90,7 +90,12 @@
} }
.visibility-level-label { .visibility-level-label {
@extend .btn;
@extend .btn-gray;
color: $gray; color: $gray;
cursor: auto;
i { i {
color: inherit; color: inherit;
} }
...@@ -178,6 +183,11 @@ ...@@ -178,6 +183,11 @@
&:active { &:active {
outline: none; outline: none;
} }
&.btn-clipboard {
padding-left: 15px;
padding-right: 15px;
}
} }
.active { .active {
...@@ -552,4 +562,4 @@ pre.light-well { ...@@ -552,4 +562,4 @@ pre.light-well {
z-index: 100; z-index: 100;
position: relative; position: relative;
} }
} }
\ No newline at end of file
...@@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController ...@@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController
if @abuse_report.save if @abuse_report.save
if current_application_settings.admin_notification_email.present? if current_application_settings.admin_notification_email.present?
AbuseReportMailer.delay.notify(@abuse_report.id) AbuseReportMailer.notify(@abuse_report.id).deliver_later
end end
message = "Thank you for your report. A GitLab administrator will look into it shortly." message = "Thank you for your report. A GitLab administrator will look into it shortly."
......
class AutocompleteController < ApplicationController class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users] skip_before_action :authenticate_user!, only: [:users]
before_action :find_users, only: [:users]
def users def users
begin
@users =
if params[:project_id].present?
project = Project.find(params[:project_id])
if can?(current_user, :read_project, project)
project.team.users
end
elsif params[:group_id]
group = Group.find(params[:group_id])
if can?(current_user, :read_group, group)
group.users
end
elsif current_user
User.all
end
rescue ActiveRecord::RecordNotFound
if current_user
return render json: {}, status: 404
end
end
if @users.nil? && current_user.nil?
authenticate_user!
end
@users ||= User.none @users ||= User.none
@users = @users.search(params[:search]) if params[:search].present? @users = @users.search(params[:search]) if params[:search].present?
@users = @users.active @users = @users.active
@users = @users.reorder(:name) @users = @users.reorder(:name)
@users = @users.page(params[:page]).per(PER_PAGE) @users = @users.page(params[:page]).per(PER_PAGE)
unless params[:search].present? if params[:search].blank?
# Include current user if available to filter by "Me" # Include current user if available to filter by "Me"
if params[:current_user] && current_user if params[:current_user] && current_user
@users = [*@users, current_user].uniq @users = [*@users, current_user].uniq
...@@ -49,4 +23,25 @@ class AutocompleteController < ApplicationController ...@@ -49,4 +23,25 @@ class AutocompleteController < ApplicationController
@user = User.find(params[:id]) @user = User.find(params[:id])
render json: @user, only: [:name, :username, :id], methods: [:avatar_url] render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
end end
private
def find_users
@users =
if params[:project_id].present?
project = Project.find(params[:project_id])
return render_404 unless can?(current_user, :read_project, project)
project.team.users
elsif params[:group_id].present?
group = Group.find(params[:group_id])
return render_404 unless can?(current_user, :read_group, group)
group.users
elsif current_user
User.all
else
User.none
end
end
end end
...@@ -15,10 +15,10 @@ module Ci ...@@ -15,10 +15,10 @@ module Ci
@builds = @config_processor.builds @builds = @config_processor.builds
@status = true @status = true
end end
rescue Ci::GitlabCiYamlProcessor::ValidationError => e rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
@error = e.message @error = e.message
@status = false @status = false
rescue Exception rescue
@error = "Undefined error" @error = "Undefined error"
@status = false @status = false
end end
......
module CreatesMergeRequestForCommit
extend ActiveSupport::Concern
def new_merge_request_path
if @project.forked?
target_project = @project.forked_from_project || @project
target_branch = target_project.repository.root_ref
else
target_project = @project
target_branch = @ref
end
new_namespace_project_merge_request_path(
@project.namespace,
@project,
merge_request: {
source_project_id: @project.id,
target_project_id: target_project.id,
source_branch: @new_branch,
target_branch: target_branch
}
)
end
def create_merge_request?
params[:create_merge_request] && @new_branch != @ref
end
end
# Controller for viewing a file's blame # Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController class Projects::BlobController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include CreatesMergeRequestForCommit
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
# Raised when given an invalid file path # Raised when given an invalid file path
...@@ -22,21 +23,9 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -22,21 +23,9 @@ class Projects::BlobController < Projects::ApplicationController
end end
def create def create
result = Files::CreateService.new(@project, current_user, @commit_params).execute create_commit(Files::CreateService, success_path: after_create_path,
failure_view: :new,
if result[:status] == :success failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
flash[:notice] = "The changes have been successfully committed"
respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) }
format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render :new }
format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } }
end
end
end end
def show def show
...@@ -47,21 +36,9 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -47,21 +36,9 @@ class Projects::BlobController < Projects::ApplicationController
end end
def update def update
result = Files::UpdateService.new(@project, current_user, @commit_params).execute create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
if result[:status] == :success failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
flash[:notice] = "Your changes have been successfully committed"
respond_to do |format|
format.html { redirect_to after_edit_path }
format.json { render json: { message: "success", filePath: after_edit_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render :edit }
format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } }
end
end
end end
def preview def preview
...@@ -77,7 +54,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -77,7 +54,7 @@ class Projects::BlobController < Projects::ApplicationController
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch) redirect_to after_destroy_path
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :show render :show
...@@ -131,15 +108,51 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -131,15 +108,51 @@ class Projects::BlobController < Projects::ApplicationController
render_404 render_404
end end
def create_commit(service, success_path:, failure_view:, failure_path:)
result = service.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
respond_to do |format|
format.html { redirect_to success_path }
format.json { render json: { message: "success", filePath: success_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render failure_view }
format.json { render json: { message: "failed", filePath: failure_path } }
end
end
end
def after_create_path
@after_create_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
end
end
def after_edit_path def after_edit_path
@after_edit_path ||= @after_edit_path ||=
if from_merge_request if create_merge_request?
new_merge_request_path
elsif from_merge_request && @new_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}" "#file-path-#{hexdigest(@path)}"
elsif @target_branch.present?
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
else else
namespace_project_blob_path(@project.namespace, @project, @id) namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
end
end
def after_destroy_path
@after_destroy_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_tree_path(@project.namespace, @project, @new_branch)
end end
end end
...@@ -154,7 +167,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -154,7 +167,7 @@ class Projects::BlobController < Projects::ApplicationController
def editor_variables def editor_variables
@current_branch = @ref @current_branch = @ref
@target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref @new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
@file_path = @file_path =
if action_name.to_s == 'create' if action_name.to_s == 'create'
...@@ -174,7 +187,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -174,7 +187,7 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = { @commit_params = {
file_path: @file_path, file_path: @file_path,
current_branch: @current_branch, current_branch: @current_branch,
target_branch: @target_branch, target_branch: @new_branch,
commit_message: params[:commit_message], commit_message: params[:commit_message],
file_content: params[:content], file_content: params[:content],
file_content_encoding: params[:encoding] file_content_encoding: params[:encoding]
......
...@@ -67,7 +67,12 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -67,7 +67,12 @@ class Projects::CommitController < Projects::ApplicationController
end end
def define_show_vars def define_show_vars
@diffs = commit.diffs if params[:w].to_i == 1
@diffs = commit.diffs({ ignore_whitespace_change: true })
else
@diffs = commit.diffs
end
@notes_count = commit.notes.count @notes_count = commit.notes.count
@builds = ci_commit.builds if ci_commit @builds = ci_commit.builds if ci_commit
......
...@@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController
def show def show
@participants = @issue.participants(current_user) @participants = @issue.participants(current_user)
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
@notes = @issue.notes.with_associations.fresh @notes = @issue.notes.nonawards.with_associations.fresh
@noteable = @issue @noteable = @issue
respond_with(@issue) respond_with(@issue)
...@@ -158,12 +158,10 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -158,12 +158,10 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def issue_params def issue_params
permitted = params.require(:issue).permit( params.require(:issue).permit(
:title, :assignee_id, :position, :description, :title, :assignee_id, :position, :description,
:milestone_id, :state_event, :task_num, label_ids: [] :milestone_id, :state_event, :task_num, label_ids: []
) )
params[:issue][:title].strip! if params[:issue][:title]
permitted
end end
def bulk_update_params def bulk_update_params
......
...@@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @merge_request)
@notes = @merge_request.mr_and_commit_notes.inc_author.fresh @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh
@discussions = Note.discussions_from_notes(@notes) @discussions = Note.discussions_from_notes(@notes)
@noteable = @merge_request @noteable = @merge_request
...@@ -276,13 +276,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -276,13 +276,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def merge_request_params def merge_request_params
permitted = params.require(:merge_request).permit( params.require(:merge_request).permit(
:title, :assignee_id, :source_project_id, :source_branch, :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id, :target_project_id, :target_branch, :milestone_id,
:state_event, :description, :task_num, label_ids: [] :state_event, :description, :task_num, label_ids: []
) )
params[:merge_request][:title].strip! if params[:merge_request][:title]
permitted
end end
# Make sure merge requests created before 8.0 # Make sure merge requests created before 8.0
......
...@@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController
before_action :authorize_read_note! before_action :authorize_read_note!
before_action :authorize_create_note!, only: [:create] before_action :authorize_create_note!, only: [:create]
before_action :authorize_admin_note!, only: [:update, :destroy] before_action :authorize_admin_note!, only: [:update, :destroy]
before_action :find_current_user_notes, except: [:destroy, :delete_attachment] before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]
def index def index
current_fetched_at = Time.now.to_i current_fetched_at = Time.now.to_i
...@@ -58,6 +58,30 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -58,6 +58,30 @@ class Projects::NotesController < Projects::ApplicationController
end end
end end
def award_toggle
noteable = if note_params[:noteable_type] == "issue"
project.issues.find(note_params[:noteable_id])
else
project.merge_requests.find(note_params[:noteable_id])
end
data = {
author: current_user,
is_award: true,
note: note_params[:note].gsub(":", '')
}
note = noteable.notes.find_by(data)
if note
note.destroy
else
Notes::CreateService.new(project, current_user, note_params).execute
end
render json: { ok: true }
end
private private
def note def note
...@@ -111,6 +135,9 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -111,6 +135,9 @@ class Projects::NotesController < Projects::ApplicationController
id: note.id, id: note.id,
discussion_id: note.discussion_id, discussion_id: note.discussion_id,
html: note_to_html(note), html: note_to_html(note),
award: note.is_award,
emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "",
note: note.note,
discussion_html: note_to_discussion_html(note), discussion_html: note_to_discussion_html(note),
discussion_with_diff_html: note_to_discussion_with_diff_html(note) discussion_with_diff_html: note_to_discussion_with_diff_html(note)
} }
......
# Controller for viewing a repository's file structure # Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController class Projects::TreeController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include CreatesMergeRequestForCommit
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
before_action :require_non_empty_project, except: [:new, :create] before_action :require_non_empty_project, except: [:new, :create]
...@@ -43,7 +44,7 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -43,7 +44,7 @@ class Projects::TreeController < Projects::ApplicationController
if result && result[:status] == :success if result && result[:status] == :success
flash[:notice] = "The directory has been successfully created" flash[:notice] = "The directory has been successfully created"
respond_to do |format| respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) } format.html { redirect_to after_create_dir_path }
end end
else else
flash[:alert] = message flash[:alert] = message
...@@ -53,6 +54,8 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -53,6 +54,8 @@ class Projects::TreeController < Projects::ApplicationController
end end
end end
private
def assign_dir_vars def assign_dir_vars
@new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref @new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref
@dir_name = File.join(@path, params[:dir_name]) @dir_name = File.join(@path, params[:dir_name])
...@@ -63,4 +66,12 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -63,4 +66,12 @@ class Projects::TreeController < Projects::ApplicationController
commit_message: params[:commit_message], commit_message: params[:commit_message],
} }
end end
def after_create_dir_path
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name))
end
end
end end
...@@ -123,7 +123,7 @@ class ProjectsController < ApplicationController ...@@ -123,7 +123,7 @@ class ProjectsController < ApplicationController
::Projects::DestroyService.new(@project, current_user, {}).execute ::Projects::DestroyService.new(@project, current_user, {}).execute
flash[:alert] = "Project '#{@project.name}' was deleted." flash[:alert] = "Project '#{@project.name}' was deleted."
redirect_back_or_default(default: dashboard_projects_path, options: {}) redirect_to dashboard_projects_path
rescue Projects::DestroyService::DestroyError => ex rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), alert: ex.message redirect_to edit_project_path(@project), alert: ex.message
end end
......
class SnippetsController < ApplicationController class SnippetsController < ApplicationController
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
# Allow read snippet
before_action :authorize_read_snippet!, only: [:show, :raw]
# Allow modify snippet # Allow modify snippet
before_action :authorize_update_snippet!, only: [:edit, :update] before_action :authorize_update_snippet!, only: [:edit, :update]
...@@ -79,10 +82,14 @@ class SnippetsController < ApplicationController ...@@ -79,10 +82,14 @@ class SnippetsController < ApplicationController
[Snippet::PUBLIC, Snippet::INTERNAL]). [Snippet::PUBLIC, Snippet::INTERNAL]).
find(params[:id]) find(params[:id])
else else
PersonalSnippet.are_public.find(params[:id]) PersonalSnippet.find(params[:id])
end end
end end
def authorize_read_snippet!
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)
end
def authorize_update_snippet! def authorize_update_snippet!
return render_404 unless can?(current_user, :update_personal_snippet, @snippet) return render_404 unless can?(current_user, :update_personal_snippet, @snippet)
end end
......
...@@ -62,10 +62,10 @@ class IssuableFinder ...@@ -62,10 +62,10 @@ class IssuableFinder
if project? if project?
@project = Project.find(params[:project_id]) @project = Project.find(params[:project_id])
unless Ability.abilities.allowed?(current_user, :read_project, @project) unless Ability.abilities.allowed?(current_user, :read_project, @project)
@project = nil @project = nil
end end
else else
@project = nil @project = nil
end end
...@@ -77,11 +77,11 @@ class IssuableFinder ...@@ -77,11 +77,11 @@ class IssuableFinder
return @projects if defined?(@projects) return @projects if defined?(@projects)
if project? if project?
project @projects = project
elsif current_user && params[:authorized_only].presence && !current_user_related? elsif current_user && params[:authorized_only].presence && !current_user_related?
current_user.authorized_projects @projects = current_user.authorized_projects
else else
ProjectsFinder.new.execute(current_user) @projects = ProjectsFinder.new.execute(current_user)
end end
end end
...@@ -190,8 +190,10 @@ class IssuableFinder ...@@ -190,8 +190,10 @@ class IssuableFinder
def by_project(items) def by_project(items)
items = items =
if projects if project?
items.of_projects(projects).references(:project) items.of_projects(projects).references_project
elsif projects
items.merge(projects.reorder(nil)).join_project
else else
items.none items.none
end end
...@@ -206,7 +208,9 @@ class IssuableFinder ...@@ -206,7 +208,9 @@ class IssuableFinder
end end
def sort(items) def sort(items)
items.sort(params[:sort]) # Ensure we always have an explicit sort order (instead of inheriting
# multiple orders when combining ActiveRecord::Relation objects).
params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc)
end end
def by_assignee(items) def by_assignee(items)
......
...@@ -12,9 +12,9 @@ class NotesFinder ...@@ -12,9 +12,9 @@ class NotesFinder
when "commit" when "commit"
project.notes.for_commit_id(target_id).not_inline project.notes.for_commit_id(target_id).not_inline
when "issue" when "issue"
project.issues.find(target_id).notes.inc_author project.issues.find(target_id).notes.nonawards.inc_author
when "merge_request" when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author
when "snippet", "project_snippet" when "snippet", "project_snippet"
project.snippets.find(target_id).notes project.snippets.find(target_id).notes
else else
......
...@@ -23,17 +23,17 @@ class ProjectsFinder ...@@ -23,17 +23,17 @@ class ProjectsFinder
group = options[:group] group = options[:group]
if group if group
base, extra = group_projects(current_user, group) segments = group_projects(current_user, group)
else else
base, extra = all_projects(current_user) segments = all_projects(current_user)
end end
if base and extra if segments.length > 1
union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
Project.where("projects.id IN (#{union.to_sql})") Project.where("projects.id IN (#{union.to_sql})")
else else
base segments.first
end end
end end
......
...@@ -68,7 +68,7 @@ module ApplicationHelper ...@@ -68,7 +68,7 @@ module ApplicationHelper
end end
end end
def avatar_icon(user_or_email = nil, size = nil) def avatar_icon(user_or_email = nil, size = nil, scale = 2)
if user_or_email.is_a?(User) if user_or_email.is_a?(User)
user = user_or_email user = user_or_email
else else
...@@ -78,12 +78,12 @@ module ApplicationHelper ...@@ -78,12 +78,12 @@ module ApplicationHelper
if user if user
user.avatar_url(size) || default_avatar user.avatar_url(size) || default_avatar
else else
gravatar_icon(user_or_email, size) gravatar_icon(user_or_email, size, scale)
end end
end end
def gravatar_icon(user_email = '', size = nil) def gravatar_icon(user_email = '', size = nil, scale = 2)
GravatarService.new.execute(user_email, size) || GravatarService.new.execute(user_email, size, scale) ||
default_avatar default_avatar
end end
......
module ButtonHelper
# Output a "Copy to Clipboard" button
#
# data - Data attributes passed to `content_tag`
#
# Examples:
#
# # Define the clipboard's text
# clipboard_button(clipboard_text: "Foo")
# # => "<button class='...' data-clipboard-text='Foo'>...</button>"
#
# # Define the target element
# clipboard_button(clipboard_target: "#foo")
# # => "<button class='...' data-clipboard-target='#foo'>...</button>"
#
# See http://clipboardjs.com/#usage
def clipboard_button(data = {})
content_tag :button,
icon('clipboard'),
class: 'btn btn-xs btn-clipboard',
data: data,
type: :button
end
def http_clone_button(project)
klass = 'btn js-protocol-switch'
klass << ' active' if default_clone_protocol == 'http'
klass << ' has_tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase
content_tag :button, protocol,
class: klass,
data: {
clone: project.http_url_to_repo,
container: 'body',
html: 'true',
title: "Set a password on your account<br>to pull or push via #{protocol}"
},
type: :button
end
def ssh_clone_button(project)
klass = 'btn js-protocol-switch'
klass << ' active' if default_clone_protocol == 'ssh'
klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
content_tag :button, 'SSH',
class: klass,
data: {
clone: project.ssh_url_to_repo,
container: 'body',
html: 'true',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
},
type: :button
end
end
module ClipboardHelper
def clipboard_button
content_tag :button,
icon('clipboard'),
class: 'btn btn-xs btn-clipboard js-clipboard-trigger',
type: :button
end
end
...@@ -28,6 +28,8 @@ module EmailsHelper ...@@ -28,6 +28,8 @@ module EmailsHelper
return "View #{action.humanize.singularize}" return "View #{action.humanize.singularize}"
end end
end end
nil
end end
def color_email_diff(diffcontent) def color_email_diff(diffcontent)
......
...@@ -87,6 +87,33 @@ module IssuesHelper ...@@ -87,6 +87,33 @@ module IssuesHelper
merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ')
end end
def url_to_emoji(name)
emoji_path = ::AwardEmoji.path_to_emoji_image(name)
url_to_image(emoji_path)
rescue StandardError
""
end
def emoji_author_list(notes, current_user)
list = notes.map do |note|
note.author == current_user ? "me" : note.author.username
end
list.join(", ")
end
def emoji_list
::AwardEmoji::EMOJI_LIST
end
def note_active_class(notes, current_user)
if current_user && notes.pluck(:author_id).include?(current_user.id)
"active"
else
""
end
end
# Required for Gitlab::Markdown::IssueReferenceFilter # Required for Gitlab::Markdown::IssueReferenceFilter
module_function :url_for_issue module_function :url_for_issue
end end
...@@ -8,14 +8,6 @@ module MergeRequestsHelper ...@@ -8,14 +8,6 @@ module MergeRequestsHelper
) )
end end
def new_mr_path_for_fork_from_push_event(event)
new_namespace_project_merge_request_path(
event.project.namespace,
event.project,
new_mr_from_push_event(event, event.project.forked_from_project)
)
end
def new_mr_from_push_event(event, target_project) def new_mr_from_push_event(event, target_project)
{ {
merge_request: { merge_request: {
......
...@@ -173,8 +173,7 @@ module ProjectsHelper ...@@ -173,8 +173,7 @@ module ProjectsHelper
'unknown' 'unknown'
end end
def default_url_to_repo(project = nil) def default_url_to_repo(project = @project)
project = project || @project
current_user ? project.url_to_repo : project.http_url_to_repo current_user ? project.url_to_repo : project.http_url_to_repo
end end
......
...@@ -8,10 +8,6 @@ class BaseMailer < ActionMailer::Base ...@@ -8,10 +8,6 @@ class BaseMailer < ActionMailer::Base
default from: Proc.new { default_sender_address.format } default from: Proc.new { default_sender_address.format }
default reply_to: Proc.new { default_reply_to_address.format } default reply_to: Proc.new { default_reply_to_address.format }
def self.delay
delay_for(2.seconds)
end
def can? def can?
Ability.abilities.allowed?(current_user, action, subject) Ability.abilities.allowed?(current_user, action, subject)
end end
......
class Ability class Ability
class << self class << self
def allowed(user, subject) def allowed(user, subject)
return not_auth_abilities(user, subject) if user.nil? return anonymous_abilities(user, subject) if user.nil?
return [] unless user.kind_of?(User) return [] unless user.is_a?(User)
return [] if user.blocked? return [] if user.blocked?
case subject.class.name case subject.class.name
...@@ -20,15 +20,25 @@ class Ability ...@@ -20,15 +20,25 @@ class Ability
end.concat(global_abilities(user)) end.concat(global_abilities(user))
end end
# List of possible abilities # List of possible abilities for anonymous user
# for non-authenticated user def anonymous_abilities(user, subject)
def not_auth_abilities(user, subject) case true
project = if subject.kind_of?(Project) when subject.is_a?(PersonalSnippet)
anonymous_personal_snippet_abilities(subject)
when subject.is_a?(Project) || subject.respond_to?(:project)
anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group)
anonymous_group_abilities(subject)
else
[]
end
end
def anonymous_project_abilities(subject)
project = if subject.is_a?(Project)
subject subject
elsif subject.respond_to?(:project)
subject.project
else else
nil subject.project
end end
if project && project.public? if project && project.public?
...@@ -48,19 +58,29 @@ class Ability ...@@ -48,19 +58,29 @@ class Ability
rules - project_disabled_features_rules(project) rules - project_disabled_features_rules(project)
else else
group = if subject.kind_of?(Group) []
subject end
elsif subject.respond_to?(:group) end
subject.group
else
nil
end
if group && group.public_profile? def anonymous_group_abilities(subject)
[:read_group] group = if subject.is_a?(Group)
else subject
[] else
end subject.group
end
if group && group.public_profile?
[:read_group]
else
[]
end
end
def anonymous_personal_snippet_abilities(snippet)
if snippet.public?
[:read_personal_snippet]
else
[]
end end
end end
...@@ -280,7 +300,7 @@ class Ability ...@@ -280,7 +300,7 @@ class Ability
end end
end end
[:note, :project_snippet, :personal_snippet].each do |name| [:note, :project_snippet].each do |name|
define_method "#{name}_abilities" do |user, subject| define_method "#{name}_abilities" do |user, subject|
rules = [] rules = []
...@@ -300,6 +320,24 @@ class Ability ...@@ -300,6 +320,24 @@ class Ability
end end
end end
def personal_snippet_abilities(user, snippet)
rules = []
if snippet.author == user
rules += [
:read_personal_snippet,
:update_personal_snippet,
:admin_personal_snippet
]
end
if snippet.public? || snippet.internal?
rules << :read_personal_snippet
end
rules
end
def group_member_abilities(user, subject) def group_member_abilities(user, subject)
rules = [] rules = []
target_user = subject.user target_user = subject.user
......
...@@ -73,15 +73,23 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -73,15 +73,23 @@ class ApplicationSetting < ActiveRecord::Base
end end
after_commit do after_commit do
Rails.cache.write('application_setting.last', self) Rails.cache.write(cache_key, self)
end end
def self.current def self.current
Rails.cache.fetch('application_setting.last') do Rails.cache.fetch(cache_key) do
ApplicationSetting.last ApplicationSetting.last
end end
end end
def self.expire
Rails.cache.delete(cache_key)
end
def self.cache_key
'application_setting.last'
end
def self.create_from_defaults def self.create_from_defaults
create( create(
default_projects_limit: Settings.gitlab['default_projects_limit'], default_projects_limit: Settings.gitlab['default_projects_limit'],
...@@ -99,7 +107,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -99,7 +107,7 @@ class ApplicationSetting < ActiveRecord::Base
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'], max_artifacts_size: Settings.artifacts['max_size'],
) )
end end
......
...@@ -14,11 +14,15 @@ module Ci ...@@ -14,11 +14,15 @@ module Ci
extend Ci::Model extend Ci::Model
after_commit do after_commit do
Rails.cache.write('ci_application_setting.last', self) Rails.cache.write(cache_key, self)
end
def self.expire
Rails.cache.delete(cache_key)
end end
def self.current def self.current
Rails.cache.fetch('ci_application_setting.last') do Rails.cache.fetch(cache_key) do
Ci::ApplicationSetting.last Ci::ApplicationSetting.last
end end
end end
...@@ -29,5 +33,9 @@ module Ci ...@@ -29,5 +33,9 @@ module Ci
add_pusher: Settings.gitlab_ci['add_pusher'], add_pusher: Settings.gitlab_ci['add_pusher'],
) )
end end
def self.cache_key
'ci_application_setting.last'
end
end end
end end
...@@ -97,6 +97,8 @@ module Ci ...@@ -97,6 +97,8 @@ module Ci
state_machine :status, initial: :pending do state_machine :status, initial: :pending do
after_transition any => [:success, :failed, :canceled] do |build, transition| after_transition any => [:success, :failed, :canceled] do |build, transition|
return unless build.gl_project
project = build.project project = build.project
if project.web_hooks? if project.web_hooks?
......
...@@ -188,13 +188,13 @@ module Ci ...@@ -188,13 +188,13 @@ module Ci
end end
def config_processor def config_processor
return nil unless ci_yaml_file
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
rescue Ci::GitlabCiYamlProcessor::ValidationError => e rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
save_yaml_error(e.message) save_yaml_error(e.message)
nil nil
rescue Exception => e rescue
logger.error e.message + "\n" + e.backtrace.join("\n") save_yaml_error("Undefined error")
save_yaml_error("Undefined yaml error")
nil nil
end end
......
...@@ -8,6 +8,7 @@ module Issuable ...@@ -8,6 +8,7 @@ module Issuable
extend ActiveSupport::Concern extend ActiveSupport::Concern
include Participable include Participable
include Mentionable include Mentionable
include StripAttribute
included do included do
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
...@@ -35,6 +36,9 @@ module Issuable ...@@ -35,6 +36,9 @@ module Issuable
scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') } scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') }
scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') } scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') }
scope :join_project, -> { joins(:project) }
scope :references_project, -> { references(:project) }
delegate :name, delegate :name,
:email, :email,
to: :author, to: :author,
...@@ -49,6 +53,7 @@ module Issuable ...@@ -49,6 +53,7 @@ module Issuable
attr_mentionable :title, pipeline: :single_line attr_mentionable :title, pipeline: :single_line
attr_mentionable :description, cache: true attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations participant :author, :assignee, :notes_with_associations
strip_attributes :title
end end
module ClassMethods module ClassMethods
...@@ -90,39 +95,14 @@ module Issuable ...@@ -90,39 +95,14 @@ module Issuable
opened? || reopened? opened? || reopened?
end end
# # Deprecated. Still exists to preserve API compatibility.
# Votes
#
# Return the number of -1 comments (downvotes)
def downvotes def downvotes
filter_superceded_votes(notes.select(&:downvote?), notes).size 0
end end
def downvotes_in_percent # Deprecated. Still exists to preserve API compatibility.
if votes_count.zero?
0
else
100.0 - upvotes_in_percent
end
end
# Return the number of +1 comments (upvotes)
def upvotes def upvotes
filter_superceded_votes(notes.select(&:upvote?), notes).size 0
end
def upvotes_in_percent
if votes_count.zero?
0
else
100.0 / votes_count * upvotes
end
end
# Return the total number of votes
def votes_count
upvotes + downvotes
end end
def subscribed?(user) def subscribed?(user)
...@@ -185,17 +165,8 @@ module Issuable ...@@ -185,17 +165,8 @@ module Issuable
notes.includes(:author, :project) notes.includes(:author, :project)
end end
private def updated_tasks
Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
def filter_superceded_votes(votes, notes) new_content: description)
filteredvotes = [] + votes
votes.each do |vote|
if vote.superceded?(notes)
filteredvotes.delete(vote)
end
end
filteredvotes
end end
end end
# == Strip Attribute module
#
# Contains functionality to clean attributes before validation
#
# Usage:
#
# class Milestone < ActiveRecord::Base
# strip_attributes :title
# end
#
#
module StripAttribute
extend ActiveSupport::Concern
module ClassMethods
def strip_attributes(*attrs)
strip_attrs.concat(attrs)
end
def strip_attrs
@strip_attrs ||= []
end
end
included do
before_validation :strip_attributes
end
def strip_attributes
self.class.strip_attrs.each do |attr|
self[attr].strip! if self[attr] && self[attr].respond_to?(:strip!)
end
end
end
...@@ -7,14 +7,39 @@ require 'task_list/filter' ...@@ -7,14 +7,39 @@ require 'task_list/filter'
# #
# Used by MergeRequest and Issue # Used by MergeRequest and Issue
module Taskable module Taskable
COMPLETED = 'completed'.freeze
INCOMPLETE = 'incomplete'.freeze
ITEM_PATTERN = /
^
(?:\s*[-+*]|(?:\d+\.))? # optional list prefix
\s* # optional whitespace prefix
(\[\s\]|\[[xX]\]) # checkbox
(\s.+) # followed by whitespace and some text.
/x
def self.get_tasks(content)
content.to_s.scan(ITEM_PATTERN).map do |checkbox, label|
# ITEM_PATTERN strips out the hyphen, but Item requires it. Rabble rabble.
TaskList::Item.new("- #{checkbox}", label.strip)
end
end
def self.get_updated_tasks(old_content:, new_content:)
old_tasks, new_tasks = get_tasks(old_content), get_tasks(new_content)
new_tasks.select.with_index do |new_task, i|
old_task = old_tasks[i]
next unless old_task
new_task.source == old_task.source && new_task.complete? != old_task.complete?
end
end
# Called by `TaskList::Summary` # Called by `TaskList::Summary`
def task_list_items def task_list_items
return [] if description.blank? return [] if description.blank?
@task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item| @task_list_items ||= Taskable.get_tasks(description)
# ItemPattern strips out the hyphen, but Item requires it. Rabble rabble.
TaskList::Item.new("- #{item}")
end
end end
def tasks def tasks
......
...@@ -134,6 +134,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -134,6 +134,9 @@ class MergeRequest < ActiveRecord::Base
scope :closed, -> { with_state(:closed) } scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) }
scope :join_project, -> { joins(:target_project) }
scope :references_project, -> { references(:target_project) }
def self.reference_prefix def self.reference_prefix
'!' '!'
end end
...@@ -473,7 +476,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -473,7 +476,7 @@ class MergeRequest < ActiveRecord::Base
end end
def ci_commit def ci_commit
if last_commit if last_commit and source_project
source_project.ci_commit(last_commit.id) source_project.ci_commit(last_commit.id)
end end
end end
......
...@@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base ...@@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base
include InternalId include InternalId
include Sortable include Sortable
include StripAttribute
belongs_to :project belongs_to :project
has_many :issues has_many :issues
...@@ -35,6 +36,8 @@ class Milestone < ActiveRecord::Base ...@@ -35,6 +36,8 @@ class Milestone < ActiveRecord::Base
validates :title, presence: true validates :title, presence: true
validates :project, presence: true validates :project, presence: true
strip_attributes :title
state_machine :state, initial: :active do state_machine :state, initial: :active do
event :close do event :close do
transition active: :closed transition active: :closed
......
...@@ -40,16 +40,20 @@ class Note < ActiveRecord::Base ...@@ -40,16 +40,20 @@ class Note < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true delegate :name, :email, to: :author, prefix: true
validates :note, :project, presence: true validates :note, :project, presence: true
validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award }
validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
# Attachments are deprecated and are handled by Markdown uploader # Attachments are deprecated and are handled by Markdown uploader
validates :attachment, file_size: { maximum: :max_attachment_size } validates :attachment, file_size: { maximum: :max_attachment_size }
validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
validates :author, presence: true
mount_uploader :attachment, AttachmentUploader mount_uploader :attachment, AttachmentUploader
# Scopes # Scopes
scope :awards, ->{ where(is_award: true) }
scope :nonawards, ->{ where(is_award: false) }
scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
scope :inline, ->{ where("line_code IS NOT NULL") } scope :inline, ->{ where("line_code IS NOT NULL") }
scope :not_inline, ->{ where(line_code: [nil, '']) } scope :not_inline, ->{ where(line_code: [nil, '']) }
...@@ -97,6 +101,12 @@ class Note < ActiveRecord::Base ...@@ -97,6 +101,12 @@ class Note < ActiveRecord::Base
def search(query) def search(query)
where("LOWER(note) like :query", query: "%#{query.downcase}%") where("LOWER(note) like :query", query: "%#{query.downcase}%")
end end
def grouped_awards
awards.select(:note).distinct.map do |note|
[ note.note, where(note: note.note) ]
end
end
end end
def cross_reference? def cross_reference?
...@@ -288,44 +298,6 @@ class Note < ActiveRecord::Base ...@@ -288,44 +298,6 @@ class Note < ActiveRecord::Base
nil nil
end end
DOWNVOTES = %w(-1 :-1: :thumbsdown: :thumbs_down_sign:)
# Check if the note is a downvote
def downvote?
votable? && note.start_with?(*DOWNVOTES)
end
UPVOTES = %w(+1 :+1: :thumbsup: :thumbs_up_sign:)
# Check if the note is an upvote
def upvote?
votable? && note.start_with?(*UPVOTES)
end
def superceded?(notes)
return false unless vote?
notes.each do |note|
next if note == self
if note.vote? &&
self[:author_id] == note[:author_id] &&
self[:created_at] <= note[:created_at]
return true
end
end
false
end
def vote?
upvote? || downvote?
end
def votable?
for_issue? || (for_merge_request? && !for_diff_line?)
end
# Mentionable override. # Mentionable override.
def gfm_reference(from_project = nil) def gfm_reference(from_project = nil)
noteable.gfm_reference(from_project) noteable.gfm_reference(from_project)
...@@ -363,6 +335,16 @@ class Note < ActiveRecord::Base ...@@ -363,6 +335,16 @@ class Note < ActiveRecord::Base
read_attribute(:system) read_attribute(:system)
end end
# Deprecated. Still exists to preserve API compatibility.
def downvote?
false
end
# Deprecated. Still exists to preserve API compatibility.
def upvote?
false
end
def editable? def editable?
!system? !system?
end end
......
...@@ -42,9 +42,8 @@ class Project < ActiveRecord::Base ...@@ -42,9 +42,8 @@ class Project < ActiveRecord::Base
include Sortable include Sortable
include AfterCommitQueue include AfterCommitQueue
include CaseSensitivity include CaseSensitivity
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Enumerize
UNKNOWN_IMPORT_URL = 'http://unknown.git' UNKNOWN_IMPORT_URL = 'http://unknown.git'
......
...@@ -64,9 +64,9 @@ module Ci ...@@ -64,9 +64,9 @@ module Ci
build.project_recipients.each do |recipient| build.project_recipients.each do |recipient|
case build.status.to_sym case build.status.to_sym
when :success when :success
mailer.build_success_email(build.id, recipient) mailer.build_success_email(build.id, recipient).deliver_later
when :failed when :failed
mailer.build_fail_email(build.id, recipient) mailer.build_fail_email(build.id, recipient).deliver_later
end end
end end
end end
...@@ -78,7 +78,7 @@ module Ci ...@@ -78,7 +78,7 @@ module Ci
end end
def mailer def mailer
Ci::Notify.delay Ci::Notify
end end
end end
end end
...@@ -32,6 +32,8 @@ class DroneCiService < CiService ...@@ -32,6 +32,8 @@ class DroneCiService < CiService
def compose_service_hook def compose_service_hook
hook = service_hook || build_service_hook hook = service_hook || build_service_hook
# If using a service template, project may not be available
hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
hook.enable_ssl_verification = enable_ssl_verification hook.enable_ssl_verification = enable_ssl_verification
hook.save hook.save
end end
......
...@@ -30,6 +30,7 @@ class GitlabCiService < CiService ...@@ -30,6 +30,7 @@ class GitlabCiService < CiService
end end
def ensure_gitlab_ci_project def ensure_gitlab_ci_project
return unless project
project.ensure_gitlab_ci_project project.ensure_gitlab_ci_project
end end
...@@ -54,7 +55,7 @@ class GitlabCiService < CiService ...@@ -54,7 +55,7 @@ class GitlabCiService < CiService
end end
def get_ci_commit(sha, ref) def get_ci_commit(sha, ref)
Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha) Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha)
end end
def commit_status(sha, ref) def commit_status(sha, ref)
......
...@@ -571,9 +571,13 @@ class Repository ...@@ -571,9 +571,13 @@ class Repository
# Run GitLab pre-receive hook # Run GitLab pre-receive hook
pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo)
status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) pre_receive_hook_status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref)
if status # Run GitLab update hook
update_hook = Gitlab::Git::Hook.new('update', path_to_repo)
update_hook_status = update_hook.trigger(gl_id, oldrev, newrev, ref)
if pre_receive_hook_status && update_hook_status
if was_empty if was_empty
# Create branch # Create branch
rugged.references.create(ref, newrev) rugged.references.create(ref, newrev)
...@@ -596,7 +600,7 @@ class Repository ...@@ -596,7 +600,7 @@ class Repository
# Remove tmp ref and return error to user # Remove tmp ref and return error to user
rugged.references.delete(tmp_ref) rugged.references.delete(tmp_ref)
raise PreReceiveError.new('Commit was rejected by pre-receive hook') raise PreReceiveError.new('Commit was rejected by git hook')
end end
end end
......
...@@ -17,9 +17,8 @@ class SentNotification < ActiveRecord::Base ...@@ -17,9 +17,8 @@ class SentNotification < ActiveRecord::Base
belongs_to :noteable, polymorphic: true belongs_to :noteable, polymorphic: true
belongs_to :recipient, class_name: "User" belongs_to :recipient, class_name: "User"
validate :project, :recipient, :reply_key, presence: true validates :project, :recipient, :reply_key, presence: true
validate :reply_key, uniqueness: true validates :reply_key, uniqueness: true
validates :noteable_id, presence: true, unless: :for_commit? validates :noteable_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit? validates :commit_id, presence: true, if: :for_commit?
validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
......
...@@ -637,11 +637,11 @@ class User < ActiveRecord::Base ...@@ -637,11 +637,11 @@ class User < ActiveRecord::Base
email.start_with?('temp-email-for-oauth') email.start_with?('temp-email-for-oauth')
end end
def avatar_url(size = nil) def avatar_url(size = nil, scale = 2)
if avatar.present? if avatar.present?
[gitlab_config.url, avatar.url].join [gitlab_config.url, avatar.url].join
else else
GravatarService.new.execute(email, size) GravatarService.new.execute(email, size, scale)
end end
end end
......
require_relative 'base_service'
class CreateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
existing_tag = repository.find_tag(tag_name)
# Only create a release if the tag exists
if existing_tag
release = project.releases.find_by(tag: tag_name)
if release
error('Release already exists', 409)
else
release = project.releases.new({ tag: tag_name, description: release_description })
release.save
success(release)
end
else
error('Tag does not exist', 404)
end
end
def success(release)
out = super()
out[:release] = release
out
end
end
...@@ -19,16 +19,16 @@ class CreateTagService < BaseService ...@@ -19,16 +19,16 @@ class CreateTagService < BaseService
new_tag = repository.find_tag(tag_name) new_tag = repository.find_tag(tag_name)
if new_tag if new_tag
if release_description
release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: release_description)
end
push_data = create_push_data(project, current_user, new_tag) push_data = create_push_data(project, current_user, new_tag)
EventCreateService.new.push(project, current_user, push_data) EventCreateService.new.push(project, current_user, push_data)
project.execute_hooks(push_data.dup, :tag_push_hooks) project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks) project.execute_services(push_data.dup, :tag_push_hooks)
if release_description
CreateReleaseService.new(@project, @current_user).
execute(tag_name, release_description)
end
success(new_tag) success(new_tag)
else else
error('Invalid reference name') error('Invalid reference name')
......
...@@ -58,12 +58,6 @@ class GitPushService ...@@ -58,12 +58,6 @@ class GitPushService
@push_data = build_push_data(oldrev, newrev, ref) @push_data = build_push_data(oldrev, newrev, ref)
# If CI was disabled but .gitlab-ci.yml file was pushed
# we enable CI automatically
if !project.builds_enabled? && gitlab_ci_yaml?(newrev)
project.enable_ci
end
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks) project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks)
...@@ -134,10 +128,4 @@ class GitPushService ...@@ -134,10 +128,4 @@ class GitPushService
def commit_user(commit) def commit_user(commit)
commit.author || user commit.author || user
end end
def gitlab_ci_yaml?(sha)
@project.repository.blob_at(sha, '.gitlab-ci.yml')
rescue Rugged::ReferenceError
nil
end
end end
class GravatarService class GravatarService
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
def execute(email, size = nil) def execute(email, size = nil, scale = 2)
if current_application_settings.gravatar_enabled? && email.present? if current_application_settings.gravatar_enabled? && email.present?
size = 40 if size.nil? || size <= 0 size = 40 if size.nil? || size <= 0
sprintf gravatar_url, sprintf gravatar_url,
hash: Digest::MD5.hexdigest(email.strip.downcase), hash: Digest::MD5.hexdigest(email.strip.downcase),
size: size, size: size * scale,
email: email.strip email: email.strip
end end
end end
......
...@@ -27,6 +27,12 @@ class IssuableBaseService < BaseService ...@@ -27,6 +27,12 @@ class IssuableBaseService < BaseService
old_branch, new_branch) old_branch, new_branch)
end end
def create_task_status_note(issuable)
issuable.updated_tasks.each do |task|
SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
end
end
def filter_params(issuable_ability_name = :issue) def filter_params(issuable_ability_name = :issue)
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
...@@ -47,14 +53,7 @@ class IssuableBaseService < BaseService ...@@ -47,14 +53,7 @@ class IssuableBaseService < BaseService
if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) if params.present? && issuable.update_attributes(params.merge(updated_by: current_user))
issuable.reset_events_cache issuable.reset_events_cache
handle_common_system_notes(issuable, old_labels: old_labels)
if issuable.labels != old_labels
create_labels_note(
issuable,
issuable.labels - old_labels,
old_labels - issuable.labels)
end
handle_changes(issuable) handle_changes(issuable)
issuable.create_new_cross_references!(current_user) issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update') execute_hooks(issuable, 'update')
...@@ -71,4 +70,19 @@ class IssuableBaseService < BaseService ...@@ -71,4 +70,19 @@ class IssuableBaseService < BaseService
close_service.new(project, current_user, {}).execute(issuable) close_service.new(project, current_user, {}).execute(issuable)
end end
end end
def handle_common_system_notes(issuable, options = {})
if issuable.previous_changes.include?('title')
create_title_change_note(issuable, issuable.previous_changes['title'].first)
end
if issuable.previous_changes.include?('description') && issuable.tasks?
create_task_status_note(issuable)
end
old_labels = options[:old_labels]
if old_labels && (issuable.labels != old_labels)
create_labels_note(issuable, issuable.labels - old_labels, old_labels - issuable.labels)
end
end
end end
...@@ -13,10 +13,6 @@ module Issues ...@@ -13,10 +13,6 @@ module Issues
create_assignee_note(issue) create_assignee_note(issue)
notification_service.reassigned_issue(issue, current_user) notification_service.reassigned_issue(issue, current_user)
end end
if issue.previous_changes.include?('title')
create_title_change_note(issue, issue.previous_changes['title'].first)
end
end end
def reopen_service def reopen_service
......
...@@ -38,7 +38,7 @@ module MergeRequests ...@@ -38,7 +38,7 @@ module MergeRequests
} }
repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options) repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options)
rescue Exception => e rescue StandardError => e
merge_request.update(merge_error: "Something went wrong during merge") merge_request.update(merge_error: "Something went wrong during merge")
Rails.logger.error(e.message) Rails.logger.error(e.message)
return false return false
......
...@@ -30,10 +30,6 @@ module MergeRequests ...@@ -30,10 +30,6 @@ module MergeRequests
notification_service.reassigned_merge_request(merge_request, current_user) notification_service.reassigned_merge_request(merge_request, current_user)
end end
if merge_request.previous_changes.include?('title')
create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
end
if merge_request.previous_changes.include?('target_branch') || if merge_request.previous_changes.include?('target_branch') ||
merge_request.previous_changes.include?('source_branch') merge_request.previous_changes.include?('source_branch')
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
......
...@@ -5,11 +5,16 @@ module Notes ...@@ -5,11 +5,16 @@ module Notes
note.author = current_user note.author = current_user
note.system = false note.system = false
if contains_emoji_only?(params[:note])
note.is_award = true
note.note = emoji_name(params[:note])
end
if note.save if note.save
notification_service.new_note(note) notification_service.new_note(note)
# Skip system notes, like status changes and cross-references. # Skip system notes, like status changes and cross-references and awards
unless note.system unless note.system || note.is_award
event_service.leave_note(note, note.author) event_service.leave_note(note, note.author)
note.create_cross_references! note.create_cross_references!
execute_hooks(note) execute_hooks(note)
...@@ -28,5 +33,13 @@ module Notes ...@@ -28,5 +33,13 @@ module Notes
note.project.execute_hooks(note_data, :note_hooks) note.project.execute_hooks(note_data, :note_hooks)
note.project.execute_services(note_data, :note_hooks) note.project.execute_services(note_data, :note_hooks)
end end
def contains_emoji_only?(note)
note =~ /\A:[-_+[:alnum:]]*:\s?\z/
end
def emoji_name(note)
note.match(/\A:([-_+[:alnum:]]*):\s?/)[1]
end
end end
end end
...@@ -13,14 +13,14 @@ class NotificationService ...@@ -13,14 +13,14 @@ class NotificationService
# even if user disabled notifications # even if user disabled notifications
def new_key(key) def new_key(key)
if key.user if key.user
mailer.new_ssh_key_email(key.id) mailer.new_ssh_key_email(key.id).deliver_later
end end
end end
# Always notify user about email added to profile # Always notify user about email added to profile
def new_email(email) def new_email(email)
if email.user if email.user
mailer.new_email_email(email.id) mailer.new_email_email(email.id).deliver_later
end end
end end
...@@ -79,17 +79,27 @@ class NotificationService ...@@ -79,17 +79,27 @@ class NotificationService
end end
def merge_mr(merge_request, current_user) def merge_mr(merge_request, current_user)
close_resource_email(merge_request, merge_request.target_project, current_user, 'merged_merge_request_email') close_resource_email(
merge_request,
merge_request.target_project,
current_user,
'merged_merge_request_email'
)
end end
def reopen_mr(merge_request, current_user) def reopen_mr(merge_request, current_user)
reopen_resource_email(merge_request, merge_request.target_project, current_user, 'merge_request_status_email', 'reopened') reopen_resource_email(
merge_request,
merge_request.target_project,
current_user, 'merge_request_status_email',
'reopened'
)
end end
# Notify new user with email after creation # Notify new user with email after creation
def new_user(user, token = nil) def new_user(user, token = nil)
# Don't email omniauth created users # Don't email omniauth created users
mailer.new_user_email(user.id, token) unless user.identities.any? mailer.new_user_email(user.id, token).deliver_later unless user.identities.any?
end end
# Notify users on new note in system # Notify users on new note in system
...@@ -102,6 +112,7 @@ class NotificationService ...@@ -102,6 +112,7 @@ class NotificationService
# ignore gitlab service messages # ignore gitlab service messages
return true if note.note.start_with?('Status changed to closed') return true if note.note.start_with?('Status changed to closed')
return true if note.cross_reference? && note.system == true return true if note.cross_reference? && note.system == true
return true if note.is_award
target = note.noteable target = note.noteable
...@@ -137,50 +148,59 @@ class NotificationService ...@@ -137,50 +148,59 @@ class NotificationService
# build notify method like 'note_commit_email' # build notify method like 'note_commit_email'
notify_method = "note_#{note.noteable_type.underscore}_email".to_sym notify_method = "note_#{note.noteable_type.underscore}_email".to_sym
recipients.each do |recipient| recipients.each do |recipient|
mailer.send(notify_method, recipient.id, note.id) mailer.send(notify_method, recipient.id, note.id).deliver_later
end end
end end
def invite_project_member(project_member, token) def invite_project_member(project_member, token)
mailer.project_member_invited_email(project_member.id, token) mailer.project_member_invited_email(project_member.id, token).deliver_later
end end
def accept_project_invite(project_member) def accept_project_invite(project_member)
mailer.project_invite_accepted_email(project_member.id) mailer.project_invite_accepted_email(project_member.id).deliver_later
end end
def decline_project_invite(project_member) def decline_project_invite(project_member)
mailer.project_invite_declined_email(project_member.project.id, project_member.invite_email, project_member.access_level, project_member.created_by_id) mailer.project_invite_declined_email(
project_member.project.id,
project_member.invite_email,
project_member.access_level,
project_member.created_by_id
).deliver_later
end end
def new_project_member(project_member) def new_project_member(project_member)
mailer.project_access_granted_email(project_member.id) mailer.project_access_granted_email(project_member.id).deliver_later
end end
def update_project_member(project_member) def update_project_member(project_member)
mailer.project_access_granted_email(project_member.id) mailer.project_access_granted_email(project_member.id).deliver_later
end end
def invite_group_member(group_member, token) def invite_group_member(group_member, token)
mailer.group_member_invited_email(group_member.id, token) mailer.group_member_invited_email(group_member.id, token).deliver_later
end end
def accept_group_invite(group_member) def accept_group_invite(group_member)
mailer.group_invite_accepted_email(group_member.id) mailer.group_invite_accepted_email(group_member.id).deliver_later
end end
def decline_group_invite(group_member) def decline_group_invite(group_member)
mailer.group_invite_declined_email(group_member.group.id, group_member.invite_email, group_member.access_level, group_member.created_by_id) mailer.group_invite_declined_email(
group_member.group.id,
group_member.invite_email,
group_member.access_level,
group_member.created_by_id
).deliver_later
end end
def new_group_member(group_member) def new_group_member(group_member)
mailer.group_access_granted_email(group_member.id) mailer.group_access_granted_email(group_member.id).deliver_later
end end
def update_group_member(group_member) def update_group_member(group_member)
mailer.group_access_granted_email(group_member.id) mailer.group_access_granted_email(group_member.id).deliver_later
end end
def project_was_moved(project, old_path_with_namespace) def project_was_moved(project, old_path_with_namespace)
...@@ -188,7 +208,11 @@ class NotificationService ...@@ -188,7 +208,11 @@ class NotificationService
recipients = reject_muted_users(recipients, project) recipients = reject_muted_users(recipients, project)
recipients.each do |recipient| recipients.each do |recipient|
mailer.project_was_moved_email(project.id, recipient.id, old_path_with_namespace) mailer.project_was_moved_email(
project.id,
recipient.id,
old_path_with_namespace
).deliver_later
end end
end end
...@@ -338,7 +362,7 @@ class NotificationService ...@@ -338,7 +362,7 @@ class NotificationService
recipients = build_recipients(target, project, target.author) recipients = build_recipients(target, project, target.author)
recipients.each do |recipient| recipients.each do |recipient|
mailer.send(method, recipient.id, target.id) mailer.send(method, recipient.id, target.id).deliver_later
end end
end end
...@@ -346,7 +370,7 @@ class NotificationService ...@@ -346,7 +370,7 @@ class NotificationService
recipients = build_recipients(target, project, current_user) recipients = build_recipients(target, project, current_user)
recipients.each do |recipient| recipients.each do |recipient|
mailer.send(method, recipient.id, target.id, current_user.id) mailer.send(method, recipient.id, target.id, current_user.id).deliver_later
end end
end end
...@@ -357,7 +381,13 @@ class NotificationService ...@@ -357,7 +381,13 @@ class NotificationService
recipients = build_recipients(target, project, current_user, [previous_assignee]) recipients = build_recipients(target, project, current_user, [previous_assignee])
recipients.each do |recipient| recipients.each do |recipient|
mailer.send(method, recipient.id, target.id, previous_assignee_id, current_user.id) mailer.send(
method,
recipient.id,
target.id,
previous_assignee_id,
current_user.id
).deliver_later
end end
end end
...@@ -365,7 +395,7 @@ class NotificationService ...@@ -365,7 +395,7 @@ class NotificationService
recipients = build_recipients(target, project, current_user) recipients = build_recipients(target, project, current_user)
recipients.each do |recipient| recipients.each do |recipient|
mailer.send(method, recipient.id, target.id, status, current_user.id) mailer.send(method, recipient.id, target.id, status, current_user.id).deliver_later
end end
end end
...@@ -387,7 +417,7 @@ class NotificationService ...@@ -387,7 +417,7 @@ class NotificationService
end end
def mailer def mailer
Notify.delay Notify
end end
def previous_record(object, attribute) def previous_record(object, attribute)
......
...@@ -341,4 +341,22 @@ class SystemNoteService ...@@ -341,4 +341,22 @@ class SystemNoteService
"* #{commit_ids} - #{commits_text} from branch `#{branch}`\n" "* #{commit_ids} - #{commits_text} from branch `#{branch}`\n"
end end
# Called when the status of a Task has changed
#
# noteable - Noteable object.
# project - Project owning noteable
# author - User performing the change
# new_task - TaskList::Item object.
#
# Example Note text:
#
# "Soandso marked the task Whatever as completed."
#
# Returns the created Note object
def self.change_task_status(noteable, project, author, new_task)
status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE
body = "Marked the task **#{new_task.source}** as #{status_label}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
end end
require_relative 'base_service'
class UpdateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
existing_tag = repository.find_tag(tag_name)
if existing_tag
release = project.releases.find_by(tag: tag_name)
if release
release.update_attributes(description: release_description)
success(release)
else
error('Release does not exist', 404)
end
else
error('Tag does not exist', 404)
end
end
def success(release)
out = super()
out[:release] = release
out
end
end
...@@ -5,15 +5,15 @@ class ArtifactUploader < CarrierWave::Uploader::Base ...@@ -5,15 +5,15 @@ class ArtifactUploader < CarrierWave::Uploader::Base
attr_accessor :build, :field attr_accessor :build, :field
def self.artifacts_path def self.artifacts_path
File.expand_path('shared/artifacts/', Rails.root) Gitlab.config.artifacts.path
end end
def self.artifacts_upload_path def self.artifacts_upload_path
File.expand_path('shared/artifacts/tmp/uploads/', Rails.root) File.join(self.artifacts_path, 'tmp/uploads/')
end end
def self.artifacts_cache_path def self.artifacts_cache_path
File.expand_path('shared/artifacts/tmp/cache/', Rails.root) File.join(self.artifacts_path, 'tmp/cache/')
end end
def initialize(build, field) def initialize(build, field)
......
- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present?
.panel.panel-default.contributed-projects
.panel-heading Projects contributed to
= render 'shared/projects/list',
projects: contributed_projects.sort_by(&:star_count).reverse,
projects_limit: 5, stars: true, avatar: false
- if local_assigns.has_key?(:projects) && projects.present?
.panel.panel-default
.panel-heading Personal projects
= render 'shared/projects/list',
projects: projects.sort_by(&:star_count).reverse,
projects_limit: 10, stars: true, avatar: false
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.row .row
.col-md-6 .col-md-6
- if @personal_projects.present? - if @personal_projects.present?
= render 'users/projects', projects: @personal_projects = render 'admin/users/projects', projects: @personal_projects
- else - else
.nothing-here-block This user has no personal projects. .nothing-here-block This user has no personal projects.
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
= label_tag :tag_list, class: 'control-label' do = label_tag :tag_list, class: 'control-label' do
Tags Tags
.col-sm-10 .col-sm-10
= f.text_field :tag_list, class: 'form-control' = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control'
.help-block You can setup builds to only use runners with specific tags .help-block You can setup builds to only use runners with specific tags
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save'
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= @project.name = @project.name
%p %p
Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)}
%p %p
Author: #{@build.commit.git_author_name} Author: #{@build.commit.git_author_name}
%p %p
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
%span{class: ("list-item-name" if show_controls)} %span{class: ("list-item-name" if show_controls)}
- if member.user - if member.user
= image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' = image_tag avatar_icon(user, 24), class: "avatar s24", alt: ''
%strong %strong
= link_to user.name, user_path(user) = link_to user.name, user_path(user)
%span.cgray= user.username %span.cgray= user.username
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%label.label.label-danger %label.label.label-danger
%strong Blocked %strong Blocked
- else - else
= image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: '' = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: ''
%strong %strong
= member.invite_email = member.invite_email
%span.cgray %span.cgray
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
- if should_user_see_group_roles?(current_user, @group) - if should_user_see_group_roles?(current_user, @group)
%span.pull-right %span.pull-right
%strong= member.human_access %strong.member-access-level= member.human_access
- if show_controls - if show_controls
- if can?(current_user, :update_group_member, member) - if can?(current_user, :update_group_member, member)
= button_tag class: "btn-xs btn js-toggle-button", = button_tag class: "btn-xs btn js-toggle-button",
......
- page_title "Members" - page_title "Members"
- header_title group_title(@group, "Members", group_group_members_path(@group)) - header_title group_title(@group, "Members", group_group_members_path(@group))
- if should_user_see_group_roles?(current_user, @group) - @blank_container = true
%p.light
Members of group have access to all group projects.
Read more about permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
.clearfix.js-toggle-container
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false }
= button_tag 'Search', class: 'btn'
.group-members-page
- if current_user && current_user.can?(:admin_group_member, @group) - if current_user && current_user.can?(:admin_group_member, @group)
.pull-right .panel.panel-default
= button_tag class: 'btn btn-new js-toggle-button', type: 'button' do .panel-heading
Add members Add new user to group
%i.fa.fa-chevron-down .panel-body
- if should_user_see_group_roles?(current_user, @group)
.js-toggle-content.hide.new-group-member-holder %p.light
= render "new_group_member" Members of group have access to all group projects.
.new-group-member-holder
.panel.panel-default.prepend-top-20 = render "new_group_member"
.panel-heading
%strong #{@group.name}
group members
%small
(#{@members.total_count})
%ul.well-list
- @members.each do |member|
= render 'groups/group_members/group_member', member: member, show_controls: true
= paginate @members, theme: 'gitlab' .panel.panel-default
.panel-heading
%strong #{@group.name}
group members
%small
(#{@members.total_count})
.pull-right
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false }
= button_tag class: 'btn', title: 'Search' do
= icon("search")
%ul.content-list
- @members.each do |member|
= render 'groups/group_members/group_member', member: member, show_controls: true
= paginate @members, theme: 'gitlab'
:javascript :javascript
$('form.member-search-form').on('submit', function(event) { $('form.member-search-form').on('submit', function(event) {
......
:plain :plain
$("##{dom_id(@member)}").replaceWith('#{escape_javascript(render(@member, member: @member, show_controls: true))}'); $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render(@group_member, member: @group_member, show_controls: true))}');
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
.project-home-panel.clearfix{:class => ("empty-project" if empty_repo)} .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
.project-identicon-holder .project-identicon-holder
= project_icon(@project, alt: '', class: 'project-avatar avatar s90') = project_icon(@project, alt: '', class: 'project-avatar avatar s90')
.project-home-desc .project-home-desc
...@@ -12,8 +12,10 @@ ...@@ -12,8 +12,10 @@
Forked from Forked from
= link_to project_path(forked_from_project) do = link_to project_path(forked_from_project) do
= forked_from_project.namespace.try(:name) = forked_from_project.namespace.try(:name)
.cover-controls
.visibility-level-label
= visibility_level_icon(@project.visibility_level)
= visibility_level_label(@project.visibility_level)
.project-repo-buttons .project-repo-buttons
.split-one .split-one
...@@ -21,7 +23,7 @@ ...@@ -21,7 +23,7 @@
= render 'projects/buttons/fork' = render 'projects/buttons/fork'
= render "shared/clone_panel" = render "shared/clone_panel"
.split-repo-buttons .split-repo-buttons
= render "projects/buttons/download" = render "projects/buttons/download"
= render 'projects/buttons/dropdown' = render 'projects/buttons/dropdown'
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%a.js-md-write-button(href="#md-write-holder" tabindex="-1") %a.js-md-write-button(href="#md-write-holder" tabindex="-1")
Write Write
%li %li
%a.js-md-preview-button(href="md-preview-holder" tabindex="-1") %a.js-md-preview-button(href="#md-preview-holder" tabindex="-1")
Preview Preview
- if defined?(referenced_users) && referenced_users - if defined?(referenced_users) && referenced_users
......
...@@ -2,9 +2,12 @@ ...@@ -2,9 +2,12 @@
%input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
.zen-backdrop .zen-backdrop
- classes << ' js-gfm-input markdown-area' - classes << ' js-gfm-input markdown-area'
= f.text_area attr, class: classes, placeholder: '' - if defined?(f) && f
= f.text_area attr, class: classes, placeholder: ''
- else
= text_area_tag attr, nil, class: classes, placeholder: ''
%a.zen-enter-link(tabindex="-1" href="#") %a.zen-enter-link(tabindex="-1" href="#")
%i.fa.fa-expand = icon('expand')
Edit in fullscreen Edit in fullscreen
%a.zen-leave-link(href="#") %a.zen-leave-link(href="#")
%i.fa.fa-compress = icon('compress')
...@@ -19,4 +19,4 @@ ...@@ -19,4 +19,4 @@
- if allowed_tree_edit? - if allowed_tree_edit?
.btn-group{ role: "group" } .btn-group{ role: "group" }
%button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Remove %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
...@@ -5,21 +5,19 @@ ...@@ -5,21 +5,19 @@
%a.close{href: "#", "data-dismiss" => "modal"} × %a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Create New Directory %h3.page-title Create New Directory
.modal-body .modal-body
= form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, id: 'dir-create-form', class: 'form-horizontal' do = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do
.form-group .form-group
= label_tag :dir_name, 'Directory Name', class: 'control-label' = label_tag :dir_name, 'Directory Name', class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control' = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control'
= render 'shared/commit_message_container', params: params, placeholder: ''
- unless @project.empty_repo? = render 'shared/new_commit_form', placeholder: "Add new directory"
.form-group
= label_tag :branch_name, 'Branch', class: 'control-label'
.col-sm-10
= text_field_tag 'new_branch', @ref, class: "form-control"
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
= submit_tag "Create directory", class: 'btn btn-primary btn-create' = submit_tag "Create directory", class: 'btn btn-primary btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:javascript :javascript
disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create"); disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create");
new NewCommitForm($('.js-create-dir-form'))
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
.modal-content .modal-content
.modal-header .modal-header
%a.close{href: "#", "data-dismiss" => "modal"} × %a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Remove #{@blob.name} %h3.page-title Delete #{@blob.name}
%p.light
From branch
%strong= @ref
.modal-body .modal-body
= form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-requires-input' do = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-requires-input' do
= render 'shared/commit_message_container', params: params, = render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}"
placeholder: 'Removed this file because...'
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
= button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' = button_tag 'Delete file', class: 'btn btn-remove btn-remove-file'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:javascript
new NewCommitForm($('.js-replace-blob-form'))
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%a.close{href: "#", "data-dismiss" => "modal"} × %a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title #{title} %h3.page-title #{title}
.modal-body .modal-body
= form_tag form_path, method: method, class: 'blob-file-upload-form-js form-horizontal' do = form_tag form_path, method: method, class: 'js-upload-blob-form form-horizontal' do
.dropzone .dropzone
.dropzone-previews.blob-upload-dropzone-previews .dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light %p.dz-message.light
...@@ -13,19 +13,15 @@ ...@@ -13,19 +13,15 @@
= link_to 'click to upload', '#', class: "markdown-selector" = link_to 'click to upload', '#', class: "markdown-selector"
%br %br
.dropzone-alerts{class: "alert alert-danger data", style: "display:none"} .dropzone-alerts{class: "alert alert-danger data", style: "display:none"}
= render 'shared/commit_message_container', params: params,
placeholder: placeholder = render 'shared/new_commit_form', placeholder: placeholder
- unless @project.empty_repo?
.form-group.branch
= label_tag 'branch', class: 'control-label' do
Branch
.col-sm-10
= text_field_tag 'new_branch', @ref, class: "form-control"
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
= button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:javascript :javascript
disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'); disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}'); new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
new NewCommitForm($('.js-upload-blob-form'))
...@@ -13,15 +13,9 @@ ...@@ -13,15 +13,9 @@
%i.fa.fa-eye %i.fa.fa-eye
= editing_preview_title(@blob.name) = editing_preview_title(@blob.name)
= form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input') do = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/commit_message_container', params: params, placeholder: "Update #{@blob.name}" = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
.form-group.branch
= label_tag 'branch', class: 'control-label' do
Branch
.col-sm-10
= text_field_tag 'new_branch', @ref, class: "form-control"
= hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'content', '', id: "file-content"
...@@ -30,3 +24,4 @@ ...@@ -30,3 +24,4 @@
:javascript :javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}") blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
new NewCommitForm($('.js-edit-blob-form'))
...@@ -2,20 +2,13 @@ ...@@ -2,20 +2,13 @@
= render "header_title" = render "header_title"
.gray-content-block.top-block .gray-content-block.top-block
Create a new file %h3.page-title
Create New File
.file-editor .file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do
= render 'projects/blob/editor', ref: @ref = render 'projects/blob/editor', ref: @ref
= render 'shared/commit_message_container', params: params, = render 'shared/new_commit_form', placeholder: "Add new file"
placeholder: 'Add new file'
- unless @project.empty_repo?
.form-group.branch
= label_tag 'branch', class: 'control-label' do
Branch
.col-sm-10
= text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit"
= hidden_field_tag 'content', '', id: 'file-content' = hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref, = render 'projects/commit_button', ref: @ref,
...@@ -23,3 +16,4 @@ ...@@ -23,3 +16,4 @@
:javascript :javascript
blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null) blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)
new NewCommitForm($('.js-new-blob-form'))
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100644 to 100755
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100644 to 100755
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment