mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-17 08:36:06 +10:00
Merge remote-tracking branch 'origin/master' into with-pipeline-view
This commit is contained in:
@@ -115,6 +115,11 @@ bundler:audit:
|
||||
script:
|
||||
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
|
||||
|
||||
db-migrate-reset:
|
||||
stage: test
|
||||
script:
|
||||
- RAILS_ENV=test bundle exec rake db:migrate:reset
|
||||
|
||||
# Ruby 2.2 jobs
|
||||
|
||||
spec:feature:ruby22:
|
||||
|
||||
+1
-1
@@ -728,7 +728,7 @@ Metrics/ParameterLists:
|
||||
# A complexity metric geared towards measuring complexity for a human reader.
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: true
|
||||
Max: 17
|
||||
Max: 18
|
||||
|
||||
|
||||
#################### Lint ################################
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ linters:
|
||||
|
||||
# Reports when you have an empty rule set.
|
||||
EmptyRule:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
# Reports when you have an @extend directive.
|
||||
ExtendDirective:
|
||||
|
||||
@@ -1,61 +1,147 @@
|
||||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.7.0 (unreleased)
|
||||
- All service classes (those residing in app/services) are now instrumented (Yorick Peterse)
|
||||
- Developers can now add custom tags to transactions (Yorick Peterse)
|
||||
v 8.8.0 (unreleased)
|
||||
- Remove future dates from contribution calendar graph.
|
||||
- Fix error when visiting commit builds page before build was updated
|
||||
- Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
|
||||
|
||||
v 8.7.1 (unreleased)
|
||||
- Throttle the update of `project.last_activity_at` to 1 minute. !3848
|
||||
- Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849
|
||||
- Fix license detection to detect all license files, not only known licenses. !3878
|
||||
- Use the `can?` helper instead of `current_user.can?`. !3882
|
||||
- Prevent users from deleting Webhooks via API they do not own
|
||||
- Fix Error 500 due to stale cache when projects are renamed or transferred
|
||||
|
||||
v 8.7.0
|
||||
- Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
|
||||
- Fix vulnerability that made it possible to gain access to private labels and milestones
|
||||
- The number of InfluxDB points stored per UDP packet can now be configured
|
||||
- Fix error when cross-project label reference used with non-existent project
|
||||
- Transactions for /internal/allowed now have an "action" tag set
|
||||
- Method instrumentation now uses Module#prepend instead of aliasing methods
|
||||
- Repository.clean_old_archives is now instrumented
|
||||
- Add support for environment variables on a job level in CI configuration file
|
||||
- SQL query counts are now tracked per transaction
|
||||
- The Projects::HousekeepingService class has extra instrumentation
|
||||
- All service classes (those residing in app/services) are now instrumented
|
||||
- Developers can now add custom tags to transactions
|
||||
- Loading of an issue's referenced merge requests and related branches is now done asynchronously
|
||||
- Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
|
||||
- Add support to cherry-pick any commit into any branch in the web interface (Minqi Pan)
|
||||
- Project switcher uses new dropdown styling
|
||||
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
|
||||
- Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
|
||||
- Restrict user profiles when public visibility level is restricted.
|
||||
- Add ability set due date to issues, sort and filter issues by due date (Mehmet Beydogan)
|
||||
- All images in discussions and wikis now link to their source files !3464 (Connor Shea).
|
||||
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
|
||||
- Add setting for customizing the list of trusted proxies !3524
|
||||
- Allow projects to be transfered to a lower visibility level group
|
||||
- Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
|
||||
- Improved Markdown rendering performance !3389 (Yorick Peterse)
|
||||
- Improved Markdown rendering performance !3389
|
||||
- Make shared runners text in box configurable
|
||||
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
|
||||
- API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
|
||||
- Expose project badges in project settings
|
||||
- Make /profile/keys/new redirect to /profile/keys for back-compat. !3717
|
||||
- Preserve time notes/comments have been updated at when moving issue
|
||||
- Make HTTP(s) label consistent on clone bar (Stan Hu)
|
||||
- Add support for `after_script`, requires Runner 1.2 (Kamil Trzciński)
|
||||
- Expose label description in API (Mariusz Jachimowicz)
|
||||
- Allow back dating on issues when created through the API
|
||||
- API: Ability to update a group (Robert Schilling)
|
||||
- API: Ability to move issues (Robert Schilling)
|
||||
- Fix Error 500 after renaming a project path (Stan Hu)
|
||||
- Fix a bug whith trailing slash in teamcity_url (Charles May)
|
||||
- Allow back dating on issues when created or updated through the API
|
||||
- Allow back dating on issue notes when created through the API
|
||||
- Propose license template when creating a new LICENSE file
|
||||
- API: Expose /licenses and /licenses/:key
|
||||
- Fix avatar stretching by providing a cropping feature
|
||||
- API: Expose `subscribed` for issues and merge requests (Robert Schilling)
|
||||
- Allow SAML to handle external users based on user's information !3530
|
||||
- Allow Omniauth providers to be marked as `external` !3657
|
||||
- Add endpoints to archive or unarchive a project !3372
|
||||
- Fix a bug whith trailing slash in bamboo_url
|
||||
- Add links to CI setup documentation from project settings and builds pages
|
||||
- Display project members page to all members
|
||||
- Handle nil descriptions in Slack issue messages (Stan Hu)
|
||||
- Add automated repository integrity checks (OFF by default)
|
||||
- API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
|
||||
- API: Ability to star and unstar a project (Robert Schilling)
|
||||
- Add default scope to projects to exclude projects pending deletion
|
||||
- Allow to close merge requests which source projects(forks) are deleted.
|
||||
- Ensure empty recipients are rejected in BuildsEmailService
|
||||
- Use rugged to change HEAD in Project#change_head (P.S.V.R)
|
||||
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
|
||||
- API: Fix milestone filtering by `iid` (Robert Schilling)
|
||||
- Make before_script and after_script overridable on per-job (Kamil Trzciński)
|
||||
- API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
|
||||
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
|
||||
- Better errors handling when creating milestones inside groups
|
||||
- Fix high CPU usage when PostReceive receives refs/merge-requests/<id>
|
||||
- Hide `Create a group` help block when creating a new project in a group
|
||||
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
|
||||
- Allow issues and merge requests to be assigned to the author !2765
|
||||
- Make Ci::Commit to group only similar builds and make it stateful (ref, tag)
|
||||
- Gracefully handle notes on deleted commits in merge requests (Stan Hu)
|
||||
- Decouple membership and notifications
|
||||
- Fix creation of merge requests for orphaned branches (Stan Hu)
|
||||
- API: Ability to retrieve a single tag (Robert Schilling)
|
||||
- While signing up, don't persist the user password across form redisplays
|
||||
- Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
|
||||
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
|
||||
- Fix admin/projects when using visibility levels on search (PotHix)
|
||||
- Build status notifications
|
||||
- Update email confirmation interface
|
||||
- API: Expose user location (Robert Schilling)
|
||||
- API: Do not leak group existence via return code (Robert Schilling)
|
||||
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
|
||||
- Update number of Todos in the sidebar when it's marked as "Done". !3600
|
||||
- Sanitize branch names created for confidential issues
|
||||
- API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
|
||||
- API: User can leave a project through the API when not master or owner. !3613
|
||||
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
|
||||
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
|
||||
- Improved markdown forms
|
||||
- Diff design updates (colors, button styles, etc)
|
||||
- Copying and pasting a diff no longer pastes the line numbers or +/-
|
||||
- Add null check to formData when updating profile content to fix Firefox bug
|
||||
- Disable spellcheck and autocorrect for username field in admin page
|
||||
- Delete tags using Rugged for performance reasons (Robert Schilling)
|
||||
- Add Slack notifications when Wiki is edited (Sebastian Klier)
|
||||
- Diffs load at the correct point when linking from from number
|
||||
- Selected diff rows highlight
|
||||
- Fix emoji categories in the emoji picker
|
||||
- API: Properly display annotated tags for GET /projects/:id/repository/tags (Robert Schilling)
|
||||
- Add encrypted credentials for imported projects and migrate old ones
|
||||
- Properly format all merge request references with ! rather than # !3740 (Ben Bodenmiller)
|
||||
- Author and participants are displayed first on users autocompletion
|
||||
- Show number sign on external issue reference text (Florent Baldino)
|
||||
- Updated print style for issues
|
||||
- Use GitHub Issue/PR number as iid to keep references
|
||||
- Import GitHub labels
|
||||
- Add option to filter by "Owned projects" on dashboard page
|
||||
- Import GitHub milestones
|
||||
- Fix emoji catgories in the emoji picker
|
||||
- Execute system web hooks on push to the project
|
||||
- Allow enable/disable push events for system hooks
|
||||
- Fix GitHub project's link in the import page when provider has a custom URL
|
||||
- Add RAW build trace output and button on build page
|
||||
- Add incremental build trace update into CI API
|
||||
|
||||
v 8.6.7
|
||||
- Fix persistent XSS vulnerability in `commit_person_link` helper
|
||||
- Fix persistent XSS vulnerability in Label and Milestone dropdowns
|
||||
- Fix vulnerability that made it possible to enumerate private projects belonging to group
|
||||
|
||||
v 8.6.6
|
||||
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
|
||||
- Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
|
||||
- Fix revoking of authorized OAuth applications (Connor Shea). !3690
|
||||
- Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
|
||||
- Project switcher uses new dropdown styling
|
||||
- Issuable header is consistent between issues and merge requests
|
||||
- Improved spacing in issuable header on mobile
|
||||
|
||||
v 8.6.5
|
||||
- Fix importing from GitHub Enterprise. !3529
|
||||
@@ -185,6 +271,9 @@ v 8.6.0
|
||||
- Trigger a todo for mentions on commits page
|
||||
- Let project owners and admins soft delete issues and merge requests
|
||||
|
||||
v 8.5.11
|
||||
- Fix persistent XSS vulnerability in `commit_person_link` helper
|
||||
|
||||
v 8.5.10
|
||||
- Fix a 2FA authentication spoofing vulnerability.
|
||||
|
||||
@@ -255,7 +344,7 @@ v 8.5.1
|
||||
|
||||
v 8.5.0
|
||||
- Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
|
||||
- Cache various Repository methods to improve performance (Yorick Peterse)
|
||||
- Cache various Repository methods to improve performance
|
||||
- Fix duplicated branch creation/deletion Webhooks/service notifications when using Web UI (Stan Hu)
|
||||
- Ensure rake tasks that don't need a DB connection can be run without one
|
||||
- Update New Relic gem to 3.14.1.311 (Stan Hu)
|
||||
@@ -332,6 +421,9 @@ v 8.5.0
|
||||
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
|
||||
- Add Todos
|
||||
|
||||
v 8.4.9
|
||||
- Fix persistent XSS vulnerability in `commit_person_link` helper
|
||||
|
||||
v 8.4.8
|
||||
- Fix a 2FA authentication spoofing vulnerability.
|
||||
|
||||
@@ -454,6 +546,9 @@ v 8.4.0
|
||||
- Add IP check against DNSBLs at account sign-up
|
||||
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
|
||||
|
||||
v 8.3.8
|
||||
- Fix persistent XSS vulnerability in `commit_person_link` helper
|
||||
|
||||
v 8.3.7
|
||||
- Fix a 2FA authentication spoofing vulnerability.
|
||||
|
||||
|
||||
@@ -323,6 +323,7 @@ request is as follows:
|
||||
[shell command guidelines](doc/development/shell_commands.md)
|
||||
1. If your code creates new files on disk please read the
|
||||
[shared files guidelines](doc/development/shared_files.md).
|
||||
1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/).
|
||||
|
||||
The **official merge window** is in the beginning of the month from the 1st to
|
||||
the 7th day of the month. This is the best time to submit an MR and get
|
||||
|
||||
@@ -178,7 +178,7 @@ gem 'ruby-fogbugz', '~> 0.2.1'
|
||||
gem 'd3_rails', '~> 3.5.0'
|
||||
|
||||
#cal-heatmap
|
||||
gem 'cal-heatmap-rails', '~> 3.5.0'
|
||||
gem 'cal-heatmap-rails', '~> 3.6.0'
|
||||
|
||||
# underscore-rails
|
||||
gem "underscore-rails", "~> 1.8.0"
|
||||
@@ -190,6 +190,9 @@ gem 'babosa', '~> 1.0.2'
|
||||
# Sanitizes SVG input
|
||||
gem "loofah", "~> 2.0.3"
|
||||
|
||||
# Working with license
|
||||
gem 'licensee', '~> 8.0.0'
|
||||
|
||||
# Protect against bruteforcing
|
||||
gem "rack-attack", '~> 4.3.1'
|
||||
|
||||
@@ -214,7 +217,7 @@ gem 'font-awesome-rails', '~> 4.2'
|
||||
gem 'gitlab_emoji', '~> 0.3.0'
|
||||
gem 'gon', '~> 6.0.1'
|
||||
gem 'jquery-atwho-rails', '~> 1.3.2'
|
||||
gem 'jquery-rails', '~> 4.0.0'
|
||||
gem 'jquery-rails', '~> 4.1.0'
|
||||
gem 'jquery-scrollto-rails', '~> 1.4.3'
|
||||
gem 'jquery-ui-rails', '~> 5.0.0'
|
||||
gem 'raphael-rails', '~> 2.1.2'
|
||||
@@ -315,7 +318,7 @@ end
|
||||
|
||||
gem "newrelic_rpm", '~> 3.14'
|
||||
|
||||
gem 'octokit', '~> 3.8.0'
|
||||
gem 'octokit', '~> 4.3.0'
|
||||
|
||||
gem "mail_room", "~> 0.6.1"
|
||||
|
||||
|
||||
+19
-17
@@ -103,7 +103,7 @@ GEM
|
||||
bundler (~> 1.2)
|
||||
thor (~> 0.18)
|
||||
byebug (8.2.1)
|
||||
cal-heatmap-rails (3.5.1)
|
||||
cal-heatmap-rails (3.6.0)
|
||||
capybara (2.6.2)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
@@ -134,7 +134,7 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.10.0)
|
||||
colorize (0.7.7)
|
||||
concurrent-ruby (1.0.0)
|
||||
concurrent-ruby (1.0.1)
|
||||
connection_pool (2.2.0)
|
||||
coveralls (0.8.13)
|
||||
json (~> 1.8)
|
||||
@@ -346,10 +346,10 @@ GEM
|
||||
flowdock (~> 0.7)
|
||||
gitlab-grit (>= 2.4.1)
|
||||
multi_json
|
||||
gitlab-grit (2.7.3)
|
||||
gitlab-grit (2.8.1)
|
||||
charlock_holmes (~> 0.6)
|
||||
diff-lcs (~> 1.1)
|
||||
mime-types (~> 1.15)
|
||||
mime-types (>= 1.16, < 3)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab_emoji (0.3.1)
|
||||
gemojione (~> 2.2, >= 2.2.1)
|
||||
@@ -431,8 +431,8 @@ GEM
|
||||
json
|
||||
ipaddress (0.8.2)
|
||||
jquery-atwho-rails (1.3.2)
|
||||
jquery-rails (4.0.5)
|
||||
rails-dom-testing (~> 1.0)
|
||||
jquery-rails (4.1.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-scrollto-rails (1.4.3)
|
||||
@@ -452,6 +452,8 @@ GEM
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.1.2)
|
||||
launchy (~> 2.2)
|
||||
licensee (8.0.0)
|
||||
rugged (>= 0.24b)
|
||||
listen (3.0.5)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
@@ -463,7 +465,7 @@ GEM
|
||||
mime-types (>= 1.16, < 4)
|
||||
mail_room (0.6.1)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mime-types (2.99.1)
|
||||
mimemagic (0.3.0)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.7.0)
|
||||
@@ -485,8 +487,8 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (~> 1.2)
|
||||
octokit (3.8.0)
|
||||
sawyer (~> 0.6.0, >= 0.5.3)
|
||||
octokit (4.3.0)
|
||||
sawyer (~> 0.7.0, >= 0.5.3)
|
||||
omniauth (1.3.1)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (>= 1.0, < 3)
|
||||
@@ -627,7 +629,7 @@ GEM
|
||||
recaptcha (1.0.2)
|
||||
json
|
||||
redcarpet (3.3.3)
|
||||
redis (3.2.2)
|
||||
redis (3.3.0)
|
||||
redis-actionpack (4.0.1)
|
||||
actionpack (~> 4)
|
||||
redis-rack (~> 1.5.0)
|
||||
@@ -712,8 +714,8 @@ GEM
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sawyer (0.6.0)
|
||||
addressable (~> 2.3.5)
|
||||
sawyer (0.7.0)
|
||||
addressable (>= 2.3.5, < 2.5)
|
||||
faraday (~> 0.8, < 0.10)
|
||||
scss_lint (0.47.1)
|
||||
rake (>= 0.9, < 11)
|
||||
@@ -734,10 +736,9 @@ GEM
|
||||
rack
|
||||
shoulda-matchers (2.8.0)
|
||||
activesupport (>= 3.0.0)
|
||||
sidekiq (4.0.1)
|
||||
sidekiq (4.1.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
json (~> 1.0)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
sidekiq-cron (0.4.0)
|
||||
redis-namespace (>= 1.5.2)
|
||||
@@ -903,7 +904,7 @@ DEPENDENCIES
|
||||
bullet
|
||||
bundler-audit
|
||||
byebug
|
||||
cal-heatmap-rails (~> 3.5.0)
|
||||
cal-heatmap-rails (~> 3.6.0)
|
||||
capybara (~> 2.6.2)
|
||||
capybara-screenshot (~> 1.0.0)
|
||||
carrierwave (~> 0.10.0)
|
||||
@@ -951,12 +952,13 @@ DEPENDENCIES
|
||||
httparty (~> 0.13.3)
|
||||
influxdb (~> 0.2)
|
||||
jquery-atwho-rails (~> 1.3.2)
|
||||
jquery-rails (~> 4.0.0)
|
||||
jquery-rails (~> 4.1.0)
|
||||
jquery-scrollto-rails (~> 1.4.3)
|
||||
jquery-turbolinks (~> 2.1.0)
|
||||
jquery-ui-rails (~> 5.0.0)
|
||||
kaminari (~> 0.16.3)
|
||||
letter_opener (~> 1.1.2)
|
||||
licensee (~> 8.0.0)
|
||||
loofah (~> 2.0.3)
|
||||
mail_room (~> 0.6.1)
|
||||
method_source (~> 0.8)
|
||||
@@ -968,7 +970,7 @@ DEPENDENCIES
|
||||
newrelic_rpm (~> 3.14)
|
||||
nokogiri (~> 1.6.7, >= 1.6.7.2)
|
||||
oauth2 (~> 1.0.0)
|
||||
octokit (~> 3.8.0)
|
||||
octokit (~> 4.3.0)
|
||||
omniauth (~> 1.3.1)
|
||||
omniauth-auth0 (~> 1.4.1)
|
||||
omniauth-azure-oauth2 (~> 0.0.6)
|
||||
|
||||
+19
@@ -105,6 +105,25 @@ sensitive as to how you word things. Use Emoji to express your feelings (heart,
|
||||
star, smile, etc.). Some good tips about giving feedback to merge requests is in
|
||||
the [Thoughtbot code review guide].
|
||||
|
||||
## Feature Freeze
|
||||
|
||||
5 working days before the 22nd the stable branches for the upcoming release will
|
||||
be frozen for major changes. Merge requests may still be merged into master
|
||||
during this period. By freezing the stable branches prior to a release there's
|
||||
no need to worry about last minute merge requests potentially breaking a lot of
|
||||
things.
|
||||
|
||||
What is considered to be a major change is determined on a case by case basis as
|
||||
this definition depends very much on the context of changes. For example, a 5
|
||||
line change might have a big impact on the entire application. Ultimately the
|
||||
decision will be made by those reviewing a merge request and the release
|
||||
manager.
|
||||
|
||||
During the feature freeze all merge requests that are meant to go into the next
|
||||
release should have the correct milestone assigned _and_ have the label
|
||||
~"Pick into Stable" set. Merge requests without a milestone and this label will
|
||||
not be merged into any stable branches.
|
||||
|
||||
## Copy & paste responses
|
||||
|
||||
### Improperly formatted issue
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
group_projects_path: "/api/:version/groups/:id/projects.json"
|
||||
projects_path: "/api/:version/projects.json"
|
||||
labels_path: "/api/:version/projects/:id/labels"
|
||||
license_path: "/api/:version/licenses/:key"
|
||||
|
||||
group: (group_id, callback) ->
|
||||
url = Api.buildUrl(Api.group_path)
|
||||
@@ -92,6 +93,16 @@
|
||||
).done (projects) ->
|
||||
callback(projects)
|
||||
|
||||
# Return text for a specific license
|
||||
licenseText: (key, data, callback) ->
|
||||
url = Api.buildUrl(Api.license_path).replace(':key', key)
|
||||
|
||||
$.ajax(
|
||||
url: url
|
||||
data: data
|
||||
).done (license) ->
|
||||
callback(license)
|
||||
|
||||
buildUrl: (url) ->
|
||||
url = gon.relative_url_root + url if gon.relative_url_root?
|
||||
return url.replace(':version', gon.api_version)
|
||||
|
||||
@@ -22,7 +22,17 @@
|
||||
#= require cal-heatmap
|
||||
#= require turbolinks
|
||||
#= require autosave
|
||||
#= require bootstrap
|
||||
#= require bootstrap/affix
|
||||
#= require bootstrap/alert
|
||||
#= require bootstrap/button
|
||||
#= require bootstrap/collapse
|
||||
#= require bootstrap/dropdown
|
||||
#= require bootstrap/modal
|
||||
#= require bootstrap/scrollspy
|
||||
#= require bootstrap/tab
|
||||
#= require bootstrap/transition
|
||||
#= require bootstrap/tooltip
|
||||
#= require bootstrap/popover
|
||||
#= require select2
|
||||
#= require raphael
|
||||
#= require g.raphael
|
||||
@@ -164,7 +174,7 @@ $ ->
|
||||
$('.trigger-submit').on 'change', ->
|
||||
$(@).parents('form').submit()
|
||||
|
||||
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false)
|
||||
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true)
|
||||
|
||||
# Flash
|
||||
if (flash = $(".flash-container")).length > 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class @AwardsHandler
|
||||
constructor: (@get_emojis_url, @post_emoji_url, @noteable_type, @noteable_id, @aliases) ->
|
||||
constructor: (@get_emojis_url, @post_emoji_url, @noteable_type, @noteable_id, @unicodes) ->
|
||||
$(".js-add-award").on "click", (event) =>
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
@@ -31,6 +31,8 @@ class @AwardsHandler
|
||||
|
||||
awards_handler.addAward emoji
|
||||
|
||||
$(this).trigger 'blur'
|
||||
|
||||
didUserClickEmoji: (that, emoji) ->
|
||||
if $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title")
|
||||
$(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title").indexOf('me') > -1
|
||||
@@ -55,7 +57,6 @@ class @AwardsHandler
|
||||
, 200
|
||||
|
||||
addAward: (emoji) ->
|
||||
emoji = @normilizeEmojiName(emoji)
|
||||
@postEmoji emoji, =>
|
||||
@addAwardToEmojiBar(emoji)
|
||||
|
||||
@@ -64,7 +65,6 @@ class @AwardsHandler
|
||||
addAwardToEmojiBar: (emoji) ->
|
||||
@addEmojiToFrequentlyUsedList(emoji)
|
||||
|
||||
emoji = @normilizeEmojiName(emoji)
|
||||
if @exist(emoji)
|
||||
if @isActive(emoji)
|
||||
@decrementCounter(emoji)
|
||||
@@ -146,15 +146,7 @@ class @AwardsHandler
|
||||
$('.award-control').tooltip()
|
||||
|
||||
resolveNameToCssClass: (emoji) ->
|
||||
emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']")
|
||||
|
||||
if emoji_icon.length > 0
|
||||
unicodeName = emoji_icon.data("unicode-name")
|
||||
else
|
||||
# Find by alias
|
||||
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data("unicode-name")
|
||||
|
||||
"emoji-#{unicodeName}"
|
||||
"emoji-#{@unicodes[emoji]}"
|
||||
|
||||
postEmoji: (emoji, callback) ->
|
||||
$.post @post_emoji_url, { note: {
|
||||
@@ -173,9 +165,6 @@ class @AwardsHandler
|
||||
scrollTop: $('.awards').offset().top - 80
|
||||
}, 200)
|
||||
|
||||
normilizeEmojiName: (emoji) ->
|
||||
@aliases[emoji] || emoji
|
||||
|
||||
addEmojiToFrequentlyUsedList: (emoji) ->
|
||||
frequently_used_emojis = @getFrequentlyUsedEmojis()
|
||||
frequently_used_emojis.push(emoji)
|
||||
|
||||
@@ -29,7 +29,11 @@ $(document).on 'keydown.quick_submit', '.js-quick-submit', (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$form = $(e.target).closest('form')
|
||||
$form.find('input[type=submit], button[type=submit]').disable()
|
||||
$submit_button = $form.find('input[type=submit], button[type=submit]')
|
||||
|
||||
return if $submit_button.attr('disabled')
|
||||
|
||||
$submit_button.disable()
|
||||
$form.submit()
|
||||
|
||||
# If the user tabs to a submit button on a `js-quick-submit` form, display a
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
class @BlobLicenseSelector
|
||||
licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i
|
||||
|
||||
constructor: (editor) ->
|
||||
@$licenseSelector = $('.js-license-selector')
|
||||
$fileNameInput = $('#file_name')
|
||||
|
||||
initialFileNameValue = if $fileNameInput.length
|
||||
$fileNameInput.val()
|
||||
else if $('.editor-file-name').length
|
||||
$('.editor-file-name').text().trim()
|
||||
|
||||
@toggleLicenseSelector(initialFileNameValue)
|
||||
|
||||
if $fileNameInput
|
||||
$fileNameInput.on 'keyup blur', (e) =>
|
||||
@toggleLicenseSelector($(e.target).val())
|
||||
|
||||
$('select.license-select').on 'change', (e) ->
|
||||
data =
|
||||
project: $(this).data('project')
|
||||
fullname: $(this).data('fullname')
|
||||
Api.licenseText $(this).val(), data, (license) ->
|
||||
editor.setValue(license.content, -1)
|
||||
|
||||
toggleLicenseSelector: (fileName) =>
|
||||
if @licenseRegex.test(fileName)
|
||||
@$licenseSelector.show()
|
||||
else
|
||||
@$licenseSelector.hide()
|
||||
@@ -1,44 +1,39 @@
|
||||
class @EditBlob
|
||||
constructor: (assets_path, mode)->
|
||||
ace.config.set "modePath", assets_path + '/ace'
|
||||
constructor: (assets_path, ace_mode = null) ->
|
||||
ace.config.set "modePath", "#{assets_path}/ace"
|
||||
ace.config.loadModule "ace/ext/searchbox"
|
||||
if mode
|
||||
ace_mode = mode
|
||||
editor = ace.edit("editor")
|
||||
editor.focus()
|
||||
@editor = editor
|
||||
|
||||
if ace_mode
|
||||
editor.getSession().setMode "ace/mode/" + ace_mode
|
||||
@editor = ace.edit("editor")
|
||||
@editor.focus()
|
||||
@editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode
|
||||
|
||||
# Before a form submission, move the content from the Ace editor into the
|
||||
# submitted textarea
|
||||
$('form').submit ->
|
||||
$("#file-content").val(editor.getValue())
|
||||
$('form').submit =>
|
||||
$("#file-content").val(@editor.getValue())
|
||||
|
||||
editModePanes = $(".js-edit-mode-pane")
|
||||
editModeLinks = $(".js-edit-mode a")
|
||||
editModeLinks.click (event) ->
|
||||
event.preventDefault()
|
||||
currentLink = $(this)
|
||||
paneId = currentLink.attr("href")
|
||||
currentPane = editModePanes.filter(paneId)
|
||||
editModeLinks.parent().removeClass "active hover"
|
||||
currentLink.parent().addClass "active hover"
|
||||
editModePanes.hide()
|
||||
if paneId is "#preview"
|
||||
currentPane.fadeIn 200
|
||||
$.post currentLink.data("preview-url"),
|
||||
content: editor.getValue()
|
||||
, (response) ->
|
||||
currentPane.empty().append response
|
||||
currentPane.syntaxHighlight()
|
||||
return
|
||||
@initModePanesAndLinks()
|
||||
new BlobLicenseSelector(@editor)
|
||||
|
||||
else
|
||||
currentPane.fadeIn 200
|
||||
editor.focus()
|
||||
return
|
||||
initModePanesAndLinks: ->
|
||||
@$editModePanes = $(".js-edit-mode-pane")
|
||||
@$editModeLinks = $(".js-edit-mode a")
|
||||
@$editModeLinks.click @editModeLinkClickHandler
|
||||
|
||||
editor: ->
|
||||
return @editor
|
||||
editModeLinkClickHandler: (event) =>
|
||||
event.preventDefault()
|
||||
currentLink = $(event.target)
|
||||
paneId = currentLink.attr("href")
|
||||
currentPane = @$editModePanes.filter(paneId)
|
||||
@$editModeLinks.parent().removeClass "active hover"
|
||||
currentLink.parent().addClass "active hover"
|
||||
@$editModePanes.hide()
|
||||
currentPane.fadeIn 200
|
||||
if paneId is "#preview"
|
||||
$.post currentLink.data("preview-url"),
|
||||
content: @editor.getValue()
|
||||
, (response) ->
|
||||
currentPane.empty().append response
|
||||
currentPane.syntaxHighlight()
|
||||
|
||||
else
|
||||
@editor.focus()
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
class @NewBlob
|
||||
constructor: (assets_path, mode)->
|
||||
ace.config.set "modePath", assets_path + '/ace'
|
||||
ace.config.loadModule "ace/ext/searchbox"
|
||||
if mode
|
||||
ace_mode = mode
|
||||
editor = ace.edit("editor")
|
||||
editor.focus()
|
||||
@editor = editor
|
||||
|
||||
if ace_mode
|
||||
editor.getSession().setMode "ace/mode/" + ace_mode
|
||||
|
||||
# Before a form submission, move the content from the Ace editor into the
|
||||
# submitted textarea
|
||||
$('form').submit ->
|
||||
$("#file-content").val(editor.getValue())
|
||||
|
||||
editor: ->
|
||||
return @editor
|
||||
@@ -1,7 +1,7 @@
|
||||
class @CommitsList
|
||||
@timer = null
|
||||
|
||||
@init: (ref, limit) ->
|
||||
@init: (limit) ->
|
||||
$("body").on "click", ".day-commits-table li.commit", (event) ->
|
||||
if event.target.nodeName != "A"
|
||||
location.href = $(this).attr("url")
|
||||
|
||||
@@ -17,6 +17,7 @@ class Dispatcher
|
||||
switch page
|
||||
when 'projects:issues:index'
|
||||
Issues.init()
|
||||
Issuable.init()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:issues:show'
|
||||
new Issue()
|
||||
@@ -28,26 +29,26 @@ class Dispatcher
|
||||
new Todos()
|
||||
when 'projects:milestones:new', 'projects:milestones:edit'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.milestone-form'))
|
||||
new GLForm($('.milestone-form'))
|
||||
when 'groups:milestones:new'
|
||||
new ZenMode()
|
||||
when 'projects:compare:show'
|
||||
new Diff()
|
||||
when 'projects:issues:new','projects:issues:edit'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new DropzoneInput($('.issue-form'))
|
||||
new GLForm($('.issue-form'))
|
||||
new IssuableForm($('.issue-form'))
|
||||
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new DropzoneInput($('.merge-request-form'))
|
||||
new GLForm($('.merge-request-form'))
|
||||
new IssuableForm($('.merge-request-form'))
|
||||
when 'projects:tags:new'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.tag-form'))
|
||||
new GLForm($('.tag-form'))
|
||||
when 'projects:releases:edit'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.release-form'))
|
||||
new GLForm($('.release-form'))
|
||||
when 'projects:merge_requests:show'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsIssuable(true)
|
||||
@@ -57,7 +58,7 @@ class Dispatcher
|
||||
new ZenMode()
|
||||
when 'projects:merge_requests:index'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
MergeRequests.init()
|
||||
Issuable.init()
|
||||
when 'dashboard:activity'
|
||||
new Activities()
|
||||
when 'dashboard:projects:starred'
|
||||
@@ -137,7 +138,7 @@ class Dispatcher
|
||||
new Wikis()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.wiki-form'))
|
||||
new GLForm($('.wiki-form'))
|
||||
when 'snippets'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode() if path[2] == 'show'
|
||||
|
||||
@@ -15,11 +15,13 @@ class @DropzoneInput
|
||||
project_uploads_path = window.project_uploads_path or null
|
||||
max_file_size = gon.max_file_size or 10
|
||||
|
||||
form_textarea = $(form).find("textarea.markdown-area")
|
||||
form_textarea = $(form).find(".js-gfm-input")
|
||||
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
|
||||
form_textarea.on 'paste', (event) =>
|
||||
handlePaste(event)
|
||||
|
||||
$mdArea = $(form_textarea).closest('.md-area')
|
||||
|
||||
$(form).setupMarkdownPreview()
|
||||
|
||||
form_dropzone = $(form).find('.div-dropzone')
|
||||
@@ -49,17 +51,17 @@ class @DropzoneInput
|
||||
$(".div-dropzone-alert").alert "close"
|
||||
|
||||
dragover: ->
|
||||
form_textarea.addClass "div-dropzone-focus"
|
||||
$mdArea.addClass 'is-dropzone-hover'
|
||||
form.find(".div-dropzone-hover").css "opacity", 0.7
|
||||
return
|
||||
|
||||
dragleave: ->
|
||||
form_textarea.removeClass "div-dropzone-focus"
|
||||
$mdArea.removeClass 'is-dropzone-hover'
|
||||
form.find(".div-dropzone-hover").css "opacity", 0
|
||||
return
|
||||
|
||||
drop: ->
|
||||
form_textarea.removeClass "div-dropzone-focus"
|
||||
$mdArea.removeClass 'is-dropzone-hover'
|
||||
form.find(".div-dropzone-hover").css "opacity", 0
|
||||
form_textarea.focus()
|
||||
return
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
class @DueDateSelect
|
||||
constructor: ->
|
||||
$loading = $('.js-issuable-update .due_date')
|
||||
.find('.block-loading')
|
||||
.hide()
|
||||
|
||||
$('.js-due-date-select').each (i, dropdown) ->
|
||||
$dropdown = $(dropdown)
|
||||
$dropdownParent = $dropdown.closest('.dropdown')
|
||||
$datePicker = $dropdownParent.find('.js-due-date-calendar')
|
||||
$block = $dropdown.closest('.block')
|
||||
$selectbox = $dropdown.closest('.selectbox')
|
||||
$value = $block.find('.value')
|
||||
$sidebarValue = $('.js-due-date-sidebar-value', $block)
|
||||
|
||||
fieldName = $dropdown.data('field-name')
|
||||
abilityName = $dropdown.data('ability-name')
|
||||
issueUpdateURL = $dropdown.data('issue-update')
|
||||
|
||||
$dropdown.glDropdown(
|
||||
hidden: ->
|
||||
$selectbox.hide()
|
||||
$value.removeAttr('style')
|
||||
)
|
||||
|
||||
addDueDate = ->
|
||||
# Create the post date
|
||||
value = $("input[name='#{fieldName}']").val()
|
||||
date = new Date value.replace(new RegExp('-', 'g'), ',')
|
||||
mediumDate = $.datepicker.formatDate 'M d, yy', date
|
||||
|
||||
data = {}
|
||||
data[abilityName] = {}
|
||||
data[abilityName].due_date = value
|
||||
|
||||
$.ajax(
|
||||
type: 'PUT'
|
||||
url: issueUpdateURL
|
||||
data: data
|
||||
beforeSend: ->
|
||||
$loading.fadeIn()
|
||||
$dropdown.trigger('loading.gl.dropdown')
|
||||
$selectbox.hide()
|
||||
$value.removeAttr('style')
|
||||
|
||||
$value.html(mediumDate)
|
||||
$sidebarValue.html(mediumDate)
|
||||
).done (data) ->
|
||||
$dropdown.trigger('loaded.gl.dropdown')
|
||||
$dropdown.dropdown('toggle')
|
||||
$loading.fadeOut()
|
||||
|
||||
$datePicker.datepicker(
|
||||
dateFormat: 'yy-mm-dd',
|
||||
defaultDate: $("input[name='#{fieldName}']").val()
|
||||
altField: "input[name='#{fieldName}']"
|
||||
onSelect: ->
|
||||
addDueDate()
|
||||
)
|
||||
|
||||
$(document)
|
||||
.off 'click', '.ui-datepicker-header a'
|
||||
.on 'click', '.ui-datepicker-header a', (e) ->
|
||||
e.stopImmediatePropagation()
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
window.GitLab ?= {}
|
||||
GitLab.GfmAutoComplete =
|
||||
dataLoading: false
|
||||
|
||||
dataSource: ''
|
||||
|
||||
# Emoji
|
||||
@@ -17,17 +19,41 @@ GitLab.GfmAutoComplete =
|
||||
template: '<li><small>${id}</small> ${title}</li>'
|
||||
|
||||
# Add GFM auto-completion to all input fields, that accept GFM input.
|
||||
setup: ->
|
||||
input = $('.js-gfm-input')
|
||||
setup: (wrap) ->
|
||||
@input = $('.js-gfm-input')
|
||||
|
||||
# destroy previous instances
|
||||
@destroyAtWho()
|
||||
|
||||
# set up instances
|
||||
@setupAtWho()
|
||||
|
||||
if @dataSource
|
||||
if !@dataLoading
|
||||
@dataLoading = true
|
||||
|
||||
# We should wait until initializations are done
|
||||
# and only trigger the last .setup since
|
||||
# The previous .dataSource belongs to the previous issuable
|
||||
# and the last one will have the **proper** .dataSource property
|
||||
# TODO: Make this a singleton and turn off events when moving to another page
|
||||
setTimeout( =>
|
||||
fetch = @fetchData(@dataSource)
|
||||
fetch.done (data) =>
|
||||
@dataLoading = false
|
||||
@loadData(data)
|
||||
, 1000)
|
||||
|
||||
|
||||
setupAtWho: ->
|
||||
# Emoji
|
||||
input.atwho
|
||||
@input.atwho
|
||||
at: ':'
|
||||
displayTpl: @Emoji.template
|
||||
insertTpl: ':${name}:'
|
||||
|
||||
# Team Members
|
||||
input.atwho
|
||||
@input.atwho
|
||||
at: '@'
|
||||
displayTpl: @Members.template
|
||||
insertTpl: '${atwho-at}${username}'
|
||||
@@ -42,7 +68,7 @@ GitLab.GfmAutoComplete =
|
||||
title: sanitize(title)
|
||||
search: sanitize("#{m.username} #{m.name}")
|
||||
|
||||
input.atwho
|
||||
@input.atwho
|
||||
at: '#'
|
||||
alias: 'issues'
|
||||
searchKey: 'search'
|
||||
@@ -55,7 +81,7 @@ GitLab.GfmAutoComplete =
|
||||
title: sanitize(i.title)
|
||||
search: "#{i.iid} #{i.title}"
|
||||
|
||||
input.atwho
|
||||
@input.atwho
|
||||
at: '!'
|
||||
alias: 'mergerequests'
|
||||
searchKey: 'search'
|
||||
@@ -68,13 +94,18 @@ GitLab.GfmAutoComplete =
|
||||
title: sanitize(m.title)
|
||||
search: "#{m.iid} #{m.title}"
|
||||
|
||||
if @dataSource
|
||||
$.getJSON(@dataSource).done (data) ->
|
||||
# load members
|
||||
input.atwho 'load', '@', data.members
|
||||
# load issues
|
||||
input.atwho 'load', 'issues', data.issues
|
||||
# load merge requests
|
||||
input.atwho 'load', 'mergerequests', data.mergerequests
|
||||
# load emojis
|
||||
input.atwho 'load', ':', data.emojis
|
||||
destroyAtWho: ->
|
||||
@input.atwho('destroy')
|
||||
|
||||
fetchData: (dataSource) ->
|
||||
$.getJSON(dataSource)
|
||||
|
||||
loadData: (data) ->
|
||||
# load members
|
||||
@input.atwho 'load', '@', data.members
|
||||
# load issues
|
||||
@input.atwho 'load', 'issues', data.issues
|
||||
# load merge requests
|
||||
@input.atwho 'load', 'mergerequests', data.mergerequests
|
||||
# load emojis
|
||||
@input.atwho 'load', ':', data.emojis
|
||||
|
||||
@@ -32,10 +32,8 @@ class GitLabDropdownFilter
|
||||
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
|
||||
$inputContainer.removeClass HAS_VALUE_CLASS
|
||||
|
||||
if keyCode is 13 and @input.val() isnt ""
|
||||
if @options.enterCallback
|
||||
@options.enterCallback()
|
||||
return
|
||||
if keyCode is 13
|
||||
return false
|
||||
|
||||
clearTimeout timeout
|
||||
timeout = setTimeout =>
|
||||
@@ -132,7 +130,6 @@ class GitLabDropdown
|
||||
@filterInput = @getElement(FILTER_INPUT)
|
||||
@highlight = false
|
||||
@filterInputBlur = true
|
||||
@enterCallback = true
|
||||
} = @options
|
||||
|
||||
self = @
|
||||
@@ -157,6 +154,9 @@ class GitLabDropdown
|
||||
@fullData = data
|
||||
|
||||
@parseData @fullData
|
||||
|
||||
if @options.filterable
|
||||
@filterInput.trigger 'keyup'
|
||||
}
|
||||
|
||||
# Init filterable
|
||||
@@ -178,9 +178,6 @@ class GitLabDropdown
|
||||
callback: (data) =>
|
||||
currentIndex = -1
|
||||
@parseData data
|
||||
enterCallback: =>
|
||||
if @enterCallback
|
||||
@selectRowAtIndex 0
|
||||
|
||||
# Event listeners
|
||||
|
||||
@@ -224,6 +221,9 @@ class GitLabDropdown
|
||||
|
||||
menu.toggleClass PAGE_TWO_CLASS
|
||||
|
||||
# Focus first visible input on active page
|
||||
@dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus()
|
||||
|
||||
parseData: (data) ->
|
||||
@renderedData = data
|
||||
|
||||
@@ -243,7 +243,8 @@ class GitLabDropdown
|
||||
shouldPropagate: (e) =>
|
||||
if @options.multiSelect
|
||||
$target = $(e.target)
|
||||
if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon')
|
||||
|
||||
if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon') and not $target.data('is-link')
|
||||
e.stopPropagation()
|
||||
return false
|
||||
else
|
||||
@@ -378,7 +379,6 @@ class GitLabDropdown
|
||||
selectedObject = @renderedData[selectedIndex]
|
||||
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
|
||||
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
|
||||
|
||||
if el.hasClass(ACTIVE_CLASS)
|
||||
el.removeClass(ACTIVE_CLASS)
|
||||
field.remove()
|
||||
@@ -389,13 +389,13 @@ class GitLabDropdown
|
||||
else
|
||||
selectedObject
|
||||
else
|
||||
if !value?
|
||||
field.remove()
|
||||
|
||||
if not @options.multiSelect
|
||||
if not @options.multiSelect or el.hasClass('dropdown-clear-active')
|
||||
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
|
||||
@dropdown.parent().find("input[name='#{fieldName}']").remove()
|
||||
|
||||
if !value?
|
||||
field.remove()
|
||||
|
||||
# Toggle active class for the tick mark
|
||||
el.addClass ACTIVE_CLASS
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
class @GLForm
|
||||
constructor: (@form) ->
|
||||
@textarea = @form.find('textarea.js-gfm-input')
|
||||
|
||||
# Before we start, we should clean up any previous data for this form
|
||||
@destroy()
|
||||
|
||||
# Setup the form
|
||||
@setupForm()
|
||||
|
||||
@form.data 'gl-form', @
|
||||
|
||||
destroy: ->
|
||||
# Clean form listeners
|
||||
@clearEventListeners()
|
||||
@form.data 'gl-form', null
|
||||
|
||||
setupForm: ->
|
||||
isNewForm = @form.is(':not(.gfm-form)')
|
||||
|
||||
@form.removeClass 'js-new-note-form'
|
||||
|
||||
if isNewForm
|
||||
@form.find('.div-dropzone').remove()
|
||||
@form.addClass('gfm-form')
|
||||
disableButtonIfEmptyField @form.find('.js-note-text'), @form.find('.js-comment-button')
|
||||
|
||||
# remove notify commit author checkbox for non-commit notes
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new DropzoneInput(@form)
|
||||
|
||||
autosize(@textarea)
|
||||
|
||||
# form and textarea event listeners
|
||||
@addEventListeners()
|
||||
|
||||
# hide discard button
|
||||
@form.find('.js-note-discard').hide()
|
||||
|
||||
@form.show()
|
||||
|
||||
clearEventListeners: ->
|
||||
@textarea.off 'focus'
|
||||
@textarea.off 'blur'
|
||||
|
||||
addEventListeners: ->
|
||||
@textarea.on 'focus', ->
|
||||
$(@).closest('.md-area').addClass 'is-focused'
|
||||
|
||||
@textarea.on 'blur', ->
|
||||
$(@).closest('.md-area').removeClass 'is-focused'
|
||||
@@ -4,18 +4,33 @@ class @ImporterStatus
|
||||
this.setAutoUpdate()
|
||||
|
||||
initStatusPage: ->
|
||||
$(".js-add-to-import").click (event) =>
|
||||
new_namespace = null
|
||||
tr = $(event.currentTarget).closest("tr")
|
||||
id = tr.attr("id").replace("repo_", "")
|
||||
if tr.find(".import-target input").length > 0
|
||||
new_namespace = tr.find(".import-target input").prop("value")
|
||||
tr.find(".import-target").empty().append(new_namespace + "/" + tr.find(".import-target").data("project_name"))
|
||||
$.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script'
|
||||
$('.js-add-to-import')
|
||||
.off 'click'
|
||||
.on 'click', (e) =>
|
||||
new_namespace = null
|
||||
$btn = $(e.currentTarget)
|
||||
$tr = $btn.closest('tr')
|
||||
id = $tr.attr('id').replace('repo_', '')
|
||||
if $tr.find('.import-target input').length > 0
|
||||
new_namespace = $tr.find('.import-target input').prop('value')
|
||||
$tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}")
|
||||
|
||||
$(".js-import-all").click (event) =>
|
||||
$(".js-add-to-import").each ->
|
||||
$(this).click()
|
||||
$btn
|
||||
.disable()
|
||||
.addClass 'is-loading'
|
||||
|
||||
$.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script'
|
||||
|
||||
$('.js-import-all')
|
||||
.off 'click'
|
||||
.on 'click', (e) ->
|
||||
$btn = $(@)
|
||||
$btn
|
||||
.disable()
|
||||
.addClass 'is-loading'
|
||||
|
||||
$('.js-add-to-import').each ->
|
||||
$(this).trigger('click')
|
||||
|
||||
setAutoUpdate: ->
|
||||
setInterval (=>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
@Issuable =
|
||||
init: ->
|
||||
Issuable.initTemplates()
|
||||
Issuable.initSearch()
|
||||
|
||||
initTemplates: ->
|
||||
Issuable.labelRow = _.template(
|
||||
'<% _.each(labels, function(label){ %>
|
||||
<span class="label-row">
|
||||
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a>
|
||||
</span>
|
||||
<% }); %>'
|
||||
)
|
||||
|
||||
initSearch: ->
|
||||
@timer = null
|
||||
$('#issue_search')
|
||||
.off 'keyup'
|
||||
.on 'keyup', ->
|
||||
clearTimeout(@timer)
|
||||
@timer = setTimeout( ->
|
||||
Issuable.filterResults $('#issue_search_form')
|
||||
, 500)
|
||||
|
||||
toggleLabelFilters: ->
|
||||
$filteredLabels = $('.filtered-labels')
|
||||
if $filteredLabels.find('.label-row').length > 0
|
||||
$filteredLabels.removeClass('hidden')
|
||||
else
|
||||
$filteredLabels.addClass('hidden')
|
||||
|
||||
filterResults: (form) =>
|
||||
formData = form.serialize()
|
||||
|
||||
$('.issues-holder, .merge-requests-holder').css('opacity', '0.5')
|
||||
formAction = form.attr('action')
|
||||
issuesUrl = formAction
|
||||
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
|
||||
issuesUrl += formData
|
||||
$.ajax
|
||||
type: 'GET'
|
||||
url: formAction
|
||||
data: formData
|
||||
complete: ->
|
||||
$('.issues-holder, .merge-requests-holder').css('opacity', '1.0')
|
||||
success: (data) ->
|
||||
$('.issues-holder, .merge-requests-holder').html(data.html)
|
||||
# Change url so if user reload a page - search results are saved
|
||||
history.replaceState {page: issuesUrl}, document.title, issuesUrl
|
||||
Issuable.reload()
|
||||
Issuable.updateStateFilters()
|
||||
$filteredLabels = $('.filtered-labels')
|
||||
|
||||
if typeof Issuable.labelRow is 'function'
|
||||
$filteredLabels.html(Issuable.labelRow(data))
|
||||
|
||||
Issuable.toggleLabelFilters()
|
||||
|
||||
dataType: "json"
|
||||
|
||||
reload: ->
|
||||
if Issues.created
|
||||
Issues.initChecks()
|
||||
|
||||
$('#filter_issue_search').val($('#issue_search').val())
|
||||
|
||||
updateStateFilters: ->
|
||||
stateFilters = $('.issues-state-filters')
|
||||
newParams = {}
|
||||
paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search']
|
||||
|
||||
for paramKey in paramKeys
|
||||
newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or ''
|
||||
|
||||
if stateFilters.length
|
||||
stateFilters.find('a').each ->
|
||||
initialUrl = gl.utils.removeParamQueryString($(this).attr('href'), 'label_name[]')
|
||||
labelNameValues = gl.utils.getParameterValues('label_name[]')
|
||||
if labelNameValues
|
||||
labelNameQueryString = ("label_name[]=#{value}" for value in labelNameValues).join('&')
|
||||
newUrl = "#{gl.utils.mergeUrlParams(newParams, initialUrl)}&#{labelNameQueryString}"
|
||||
else
|
||||
newUrl = gl.utils.mergeUrlParams(newParams, initialUrl)
|
||||
$(this).attr 'href', newUrl
|
||||
@@ -9,21 +9,29 @@ class @IssuableContext
|
||||
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
|
||||
$(this).submit()
|
||||
|
||||
$(document).off("click", ".edit-link").on "click",".edit-link", (e) ->
|
||||
$block = $(@).parents('.block')
|
||||
$selectbox = $block.find('.selectbox')
|
||||
if $selectbox.is(':visible')
|
||||
$selectbox.hide()
|
||||
$block.find('.value').show()
|
||||
else
|
||||
$selectbox.show()
|
||||
$block.find('.value').hide()
|
||||
$(document)
|
||||
.off 'click', '.issuable-sidebar .dropdown-content a'
|
||||
.on 'click', '.issuable-sidebar .dropdown-content a', (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
if $selectbox.is(':visible')
|
||||
setTimeout (->
|
||||
$block.find('.dropdown-menu-toggle').trigger 'click'
|
||||
), 0
|
||||
|
||||
$(document)
|
||||
.off 'click', '.edit-link'
|
||||
.on 'click', '.edit-link', (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$block = $(@).parents('.block')
|
||||
$selectbox = $block.find('.selectbox')
|
||||
if $selectbox.is(':visible')
|
||||
$selectbox.hide()
|
||||
$block.find('.value').show()
|
||||
else
|
||||
$selectbox.show()
|
||||
$block.find('.value').hide()
|
||||
|
||||
if $selectbox.is(':visible')
|
||||
setTimeout ->
|
||||
$block.find('.dropdown-menu-toggle').trigger 'click'
|
||||
, 0
|
||||
|
||||
$(".right-sidebar").niceScroll()
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ class @Issue
|
||||
@initTaskList()
|
||||
@initIssueBtnEventListeners()
|
||||
|
||||
@initMergeRequests()
|
||||
@initRelatedBranches()
|
||||
|
||||
initTaskList: ->
|
||||
$('.detail-page-description .js-task-list-container').taskList('enable')
|
||||
$(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
|
||||
@@ -69,3 +72,23 @@ class @Issue
|
||||
type: 'PATCH'
|
||||
url: $('form.js-issuable-update').attr('action')
|
||||
data: patchData
|
||||
|
||||
initMergeRequests: ->
|
||||
$container = $('#merge-requests')
|
||||
|
||||
$.getJSON($container.data('url'))
|
||||
.error ->
|
||||
new Flash('Failed to load referenced merge requests', 'alert')
|
||||
.success (data) ->
|
||||
if 'html' of data
|
||||
$container.html(data.html)
|
||||
|
||||
initRelatedBranches: ->
|
||||
$container = $('#related-branches')
|
||||
|
||||
$.getJSON($container.data('url'))
|
||||
.error ->
|
||||
new Flash('Failed to load related branches', 'alert')
|
||||
.success (data) ->
|
||||
if 'html' of data
|
||||
$container.html(data.html)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@Issues =
|
||||
init: ->
|
||||
Issues.initSearch()
|
||||
Issues.created = true
|
||||
Issues.initChecks()
|
||||
|
||||
$("body").on "ajax:success", ".close_issue, .reopen_issue", ->
|
||||
@@ -15,10 +15,6 @@
|
||||
else
|
||||
$(this).html totalIssues - 1
|
||||
|
||||
reload: ->
|
||||
Issues.initChecks()
|
||||
$('#filter_issue_search').val($('#issue_search').val())
|
||||
|
||||
initChecks: ->
|
||||
$(".check_all_issues").click ->
|
||||
$(".selected_issue").prop("checked", @checked)
|
||||
@@ -26,51 +22,6 @@
|
||||
|
||||
$(".selected_issue").bind "change", Issues.checkChanged
|
||||
|
||||
# Update state filters if present in page
|
||||
updateStateFilters: ->
|
||||
stateFilters = $('.issues-state-filters')
|
||||
newParams = {}
|
||||
paramKeys = ['author_id', 'label_name', 'milestone_title', 'assignee_id', 'issue_search']
|
||||
|
||||
for paramKey in paramKeys
|
||||
newParams[paramKey] = gl.utils.getUrlParameter(paramKey) or ''
|
||||
|
||||
if stateFilters.length
|
||||
stateFilters.find('a').each ->
|
||||
initialUrl = $(this).attr 'href'
|
||||
$(this).attr 'href', gl.utils.mergeUrlParams(newParams, initialUrl)
|
||||
|
||||
# Make sure we trigger ajax request only after user stop typing
|
||||
initSearch: ->
|
||||
@timer = null
|
||||
$("#issue_search").keyup ->
|
||||
clearTimeout(@timer)
|
||||
@timer = setTimeout( ->
|
||||
Issues.filterResults $("#issue_search_form")
|
||||
, 500)
|
||||
|
||||
filterResults: (form) =>
|
||||
$('.issues-holder, .merge-requests-holder').css("opacity", '0.5')
|
||||
formAction = form.attr('action')
|
||||
formData = form.serialize()
|
||||
issuesUrl = formAction
|
||||
issuesUrl += ("#{if formAction.indexOf("?") < 0 then '?' else '&'}")
|
||||
issuesUrl += formData
|
||||
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: formAction
|
||||
data: formData
|
||||
complete: ->
|
||||
$('.issues-holder, .merge-requests-holder').css("opacity", '1.0')
|
||||
success: (data) ->
|
||||
$('.issues-holder, .merge-requests-holder').html(data.html)
|
||||
# Change url so if user reload a page - search results are saved
|
||||
history.replaceState {page: issuesUrl}, document.title, issuesUrl
|
||||
Issues.reload()
|
||||
Issues.updateStateFilters()
|
||||
dataType: "json"
|
||||
|
||||
checkChanged: ->
|
||||
checked_issues = $(".selected_issue:checked")
|
||||
if checked_issues.length > 0
|
||||
|
||||
@@ -6,7 +6,7 @@ class @LabelsSelect
|
||||
labelUrl = $dropdown.data('labels')
|
||||
issueUpdateURL = $dropdown.data('issueUpdate')
|
||||
selectedLabel = $dropdown.data('selected')
|
||||
if selectedLabel?
|
||||
if selectedLabel? and not $dropdown.hasClass 'js-multiselect'
|
||||
selectedLabel = selectedLabel.split(',')
|
||||
newLabelField = $('#new_label_name')
|
||||
newColorField = $('#new_label_color')
|
||||
@@ -16,33 +16,32 @@ class @LabelsSelect
|
||||
abilityName = $dropdown.data('ability-name')
|
||||
$selectbox = $dropdown.closest('.selectbox')
|
||||
$block = $selectbox.closest('.block')
|
||||
$form = $dropdown.closest('form')
|
||||
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
|
||||
$value = $block.find('.value')
|
||||
$newLabelError = $('.js-label-error')
|
||||
$colorPreview = $('.js-dropdown-label-color-preview')
|
||||
$newLabelCreateButton = $('.js-new-label-btn')
|
||||
|
||||
$newLabelError.hide()
|
||||
$loading = $block.find('.block-loading').fadeOut()
|
||||
|
||||
if newLabelField.length
|
||||
$newLabelCreateButton = $('.js-new-label-btn')
|
||||
$colorPreview = $('.js-dropdown-label-color-preview')
|
||||
$newLabelError = $dropdown.parent().find('.js-label-error')
|
||||
$newLabelError.hide()
|
||||
|
||||
# Suggested colors in the dropdown to chose from pre-chosen colors
|
||||
$('.suggest-colors-dropdown a').on 'click', (e) ->
|
||||
|
||||
issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
|
||||
if issueUpdateURL
|
||||
labelHTMLTemplate = _.template(
|
||||
'<% _.each(labels, function(label){ %>
|
||||
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>">
|
||||
<span class="label color-label" style="background-color: <%= label.color %>;">
|
||||
<%= label.title %>
|
||||
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= _.escape(label.title) %>">
|
||||
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
|
||||
<%= _.escape(label.title) %>
|
||||
</span>
|
||||
</a>
|
||||
<% }); %>'
|
||||
);
|
||||
)
|
||||
labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
|
||||
|
||||
if newLabelField.length and $dropdown.hasClass 'js-extra-options'
|
||||
if newLabelField.length
|
||||
|
||||
# Suggested colors in the dropdown to chose from pre-chosen colors
|
||||
$('.suggest-colors-dropdown a').on "click", (e) ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -81,26 +80,25 @@ class @LabelsSelect
|
||||
enableLabelCreateButton = ->
|
||||
if newLabelField.val() isnt '' and newColorField.val() isnt ''
|
||||
$newLabelError.hide()
|
||||
$('.js-new-label-btn').disable()
|
||||
|
||||
# Create new label with API
|
||||
Api.newLabel projectId, {
|
||||
name: newLabelField.val()
|
||||
color: newColorField.val()
|
||||
}, (label) ->
|
||||
$('.js-new-label-btn').enable()
|
||||
|
||||
if label.message?
|
||||
$newLabelError
|
||||
.text label.message
|
||||
.show()
|
||||
else
|
||||
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
||||
|
||||
$newLabelCreateButton.enable()
|
||||
else
|
||||
$newLabelCreateButton.disable()
|
||||
|
||||
saveLabel = ->
|
||||
# Create new label with API
|
||||
Api.newLabel projectId, {
|
||||
name: newLabelField.val()
|
||||
color: newColorField.val()
|
||||
}, (label) ->
|
||||
$newLabelCreateButton.enable()
|
||||
|
||||
if label.message?
|
||||
$newLabelError
|
||||
.text label.message
|
||||
.show()
|
||||
else
|
||||
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
||||
|
||||
newLabelField.on 'keyup change', enableLabelCreateButton
|
||||
|
||||
newColorField.on 'keyup change', enableLabelCreateButton
|
||||
@@ -111,24 +109,7 @@ class @LabelsSelect
|
||||
.on 'click', (e) ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if newLabelField.val() isnt '' and newColorField.val() isnt ''
|
||||
$newLabelError.hide()
|
||||
$('.js-new-label-btn').disable()
|
||||
|
||||
# Create new label with API
|
||||
Api.newLabel projectId, {
|
||||
name: newLabelField.val()
|
||||
color: newColorField.val()
|
||||
}, (label) ->
|
||||
$('.js-new-label-btn').enable()
|
||||
|
||||
if label.message?
|
||||
$newLabelError
|
||||
.text label.message
|
||||
.show()
|
||||
else
|
||||
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
||||
saveLabel()
|
||||
|
||||
saveLabelData = ->
|
||||
selected = $dropdown
|
||||
@@ -165,11 +146,13 @@ class @LabelsSelect
|
||||
.html(template)
|
||||
$sidebarCollapsedValue.text(labelCount)
|
||||
|
||||
$('.has-tooltip', $value).tooltip(container: 'body')
|
||||
|
||||
$value
|
||||
.find('a')
|
||||
.each((i) ->
|
||||
setTimeout(=>
|
||||
glAnimate($(@), 'pulse')
|
||||
gl.animate.animate($(@), 'pulse')
|
||||
,200 * i
|
||||
)
|
||||
)
|
||||
@@ -198,18 +181,23 @@ class @LabelsSelect
|
||||
callback data
|
||||
|
||||
renderRow: (label) ->
|
||||
selectedClass = ''
|
||||
if $selectbox.find("input[type='hidden']\
|
||||
[name='#{$dropdown.data('field-name')}']\
|
||||
[value='#{label.id}']").length
|
||||
selectedClass = 'is-active'
|
||||
removesAll = label.id is 0 or not label.id?
|
||||
|
||||
selectedClass = []
|
||||
if $form.find("input[type='hidden']\
|
||||
[name='#{$dropdown.data('fieldName')}']\
|
||||
[value='#{this.id(label)}']").length
|
||||
selectedClass.push 'is-active'
|
||||
|
||||
if $dropdown.hasClass('js-multiselect') and removesAll
|
||||
selectedClass.push 'dropdown-clear-active'
|
||||
|
||||
color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else ""
|
||||
|
||||
"<li>
|
||||
<a href='#' class='#{selectedClass}'>
|
||||
<a href='#' class='#{selectedClass.join(' ')}'>
|
||||
#{color}
|
||||
#{label.title}
|
||||
#{_.escape(label.title)}
|
||||
</a>
|
||||
</li>"
|
||||
filterable: true
|
||||
@@ -217,37 +205,56 @@ class @LabelsSelect
|
||||
fields: ['title']
|
||||
selectable: true
|
||||
|
||||
toggleLabel: (selected) ->
|
||||
toggleLabel: (selected, el) ->
|
||||
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
|
||||
|
||||
if selected and selected.title?
|
||||
selected.title
|
||||
if selected_labels.length > 1
|
||||
"#{selected.title} +#{selected_labels.length - 1} more"
|
||||
else
|
||||
selected.title
|
||||
else if not selected and selected_labels.length isnt 0
|
||||
if selected_labels.length > 1
|
||||
"#{$(selected_labels[0]).text()} +#{selected_labels.length - 1} more"
|
||||
else if selected_labels.length is 1
|
||||
$(selected_labels).text()
|
||||
else
|
||||
defaultLabel
|
||||
fieldName: $dropdown.data('field-name')
|
||||
id: (label) ->
|
||||
if label.isAny?
|
||||
''
|
||||
else if $dropdown.hasClass "js-filter-submit"
|
||||
label.title
|
||||
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
|
||||
_.escape label.title
|
||||
else
|
||||
label.id
|
||||
|
||||
hidden: ->
|
||||
page = $('body').data 'page'
|
||||
isIssueIndex = page is 'projects:issues:index'
|
||||
isMRIndex = page is 'projects:merge_requests:index'
|
||||
|
||||
$selectbox.hide()
|
||||
# display:block overrides the hide-collapse rule
|
||||
$value.removeAttr('style')
|
||||
if $dropdown.hasClass 'js-multiselect'
|
||||
saveLabelData()
|
||||
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
|
||||
selectedLabels = $dropdown
|
||||
.closest('form')
|
||||
.find("input:hidden[name='#{$dropdown.data('fieldName')}']")
|
||||
Issuable.filterResults $dropdown.closest('form')
|
||||
else if $dropdown.hasClass('js-filter-submit')
|
||||
$dropdown.closest('form').submit()
|
||||
else
|
||||
saveLabelData()
|
||||
|
||||
multiSelect: $dropdown.hasClass 'js-multiselect'
|
||||
clicked: (label) ->
|
||||
page = $('body').data 'page'
|
||||
isIssueIndex = page is 'projects:issues:index'
|
||||
isMRIndex = page is page is 'projects:merge_requests:index'
|
||||
|
||||
isMRIndex = page is 'projects:merge_requests:index'
|
||||
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
|
||||
selectedLabel = label.title
|
||||
|
||||
Issues.filterResults $dropdown.closest('form')
|
||||
if not $dropdown.hasClass 'js-multiselect'
|
||||
selectedLabel = label.title
|
||||
Issuable.filterResults $dropdown.closest('form')
|
||||
else if $dropdown.hasClass 'js-filter-submit'
|
||||
$dropdown.closest('form').submit()
|
||||
else
|
||||
|
||||
@@ -1,13 +1,39 @@
|
||||
((w) ->
|
||||
if not w.gl? then w.gl = {}
|
||||
if not gl.animate? then gl.animate = {}
|
||||
|
||||
w.glAnimate = ($el, animation, done) ->
|
||||
gl.animate.animate = ($el, animation, options, done) ->
|
||||
if options?.cssStart?
|
||||
$el.css(options.cssStart)
|
||||
$el
|
||||
.removeClass()
|
||||
.removeClass(animation + ' animated')
|
||||
.addClass(animation + ' animated')
|
||||
.one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', ->
|
||||
$(this).removeClass()
|
||||
$(this).removeClass(animation + ' animated')
|
||||
if done?
|
||||
done()
|
||||
if options?.cssEnd?
|
||||
$el.css(options.cssEnd)
|
||||
return
|
||||
return
|
||||
return
|
||||
|
||||
gl.animate.animateEach = ($els, animation, time, options, done) ->
|
||||
dfd = $.Deferred()
|
||||
if not $els.length
|
||||
dfd.resolve()
|
||||
$els.each((i) ->
|
||||
setTimeout(=>
|
||||
$this = $(@)
|
||||
gl.animate.animate($this, animation, options, =>
|
||||
if i is $els.length - 1
|
||||
dfd.resolve()
|
||||
if done?
|
||||
done()
|
||||
)
|
||||
,time * i
|
||||
)
|
||||
return
|
||||
)
|
||||
return dfd.promise()
|
||||
return
|
||||
) window
|
||||
@@ -3,16 +3,20 @@
|
||||
w.gl ?= {}
|
||||
w.gl.utils ?= {}
|
||||
|
||||
w.gl.utils.getUrlParameter = (sParam) ->
|
||||
# Returns an array containing the value(s) of the
|
||||
# of the key passed as an argument
|
||||
w.gl.utils.getParameterValues = (sParam) ->
|
||||
sPageURL = decodeURIComponent(window.location.search.substring(1))
|
||||
sURLVariables = sPageURL.split('&')
|
||||
sParameterName = undefined
|
||||
values = []
|
||||
i = 0
|
||||
while i < sURLVariables.length
|
||||
sParameterName = sURLVariables[i].split('=')
|
||||
if sParameterName[0] is sParam
|
||||
return if sParameterName[1] is undefined then true else sParameterName[1]
|
||||
values.push(sParameterName[1])
|
||||
i++
|
||||
values
|
||||
|
||||
# #
|
||||
# @param {Object} params - url keys and value to merge
|
||||
@@ -28,4 +32,12 @@
|
||||
newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}"
|
||||
newUrl
|
||||
|
||||
# removes parameter query string from url. returns the modified url
|
||||
w.gl.utils.removeParamQueryString = (url, param) ->
|
||||
url = decodeURIComponent(url)
|
||||
urlVariables = url.split('&')
|
||||
(
|
||||
variables for variables in urlVariables when variables.indexOf(param) is -1
|
||||
).join('&')
|
||||
|
||||
) window
|
||||
|
||||
@@ -85,8 +85,10 @@ class @MergeRequestTabs
|
||||
|
||||
scrollToElement: (container) ->
|
||||
if window.location.hash
|
||||
$el = $("div#{container} #{window.location.hash}")
|
||||
$('body').scrollTo($el.offset().top) if $el.length
|
||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
||||
|
||||
$el = $("#{container} #{window.location.hash}:not(.match)")
|
||||
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
|
||||
|
||||
# Activate a tab based on the current action
|
||||
activateTab: (action) ->
|
||||
@@ -152,12 +154,39 @@ class @MergeRequestTabs
|
||||
@_get
|
||||
url: "#{source}.json" + @_location.search
|
||||
success: (data) =>
|
||||
document.querySelector("div#diffs").innerHTML = data.html
|
||||
$('#diffs').html data.html
|
||||
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
|
||||
$('div#diffs .js-syntax-highlight').syntaxHighlight()
|
||||
$('#diffs .js-syntax-highlight').syntaxHighlight()
|
||||
@expandViewContainer() if @diffViewType() is 'parallel'
|
||||
@diffsLoaded = true
|
||||
@scrollToElement("#diffs")
|
||||
@highlighSelectedLine()
|
||||
|
||||
$(document)
|
||||
.off 'click', '.diff-line-num a'
|
||||
.on 'click', '.diff-line-num a', (e) =>
|
||||
e.preventDefault()
|
||||
window.location.hash = $(e.currentTarget).attr 'href'
|
||||
@highlighSelectedLine()
|
||||
@scrollToElement("#diffs")
|
||||
|
||||
highlighSelectedLine: ->
|
||||
$('.hll').removeClass 'hll'
|
||||
locationHash = window.location.hash
|
||||
|
||||
if locationHash isnt ''
|
||||
hashClassString = ".#{locationHash.replace('#', '')}"
|
||||
$diffLine = $("#{locationHash}:not(.match)", $('#diffs'))
|
||||
|
||||
if not $diffLine.is 'tr'
|
||||
$diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}")
|
||||
else
|
||||
$diffLine = $diffLine.find('td')
|
||||
|
||||
if $diffLine.length
|
||||
$diffLine.addClass 'hll'
|
||||
diffLineTop = $diffLine.offset().top
|
||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
||||
|
||||
loadBuilds: (source) ->
|
||||
return if @buildsLoaded
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#
|
||||
# * Filter merge requests
|
||||
#
|
||||
@MergeRequests =
|
||||
init: ->
|
||||
MergeRequests.initSearch()
|
||||
|
||||
# Make sure we trigger ajax request only after user stop typing
|
||||
initSearch: ->
|
||||
@timer = null
|
||||
$("#issue_search").keyup ->
|
||||
clearTimeout(@timer)
|
||||
@timer = setTimeout(MergeRequests.filterResults, 500)
|
||||
|
||||
filterResults: =>
|
||||
form = $("#issue_search_form")
|
||||
search = $("#issue_search").val()
|
||||
$('.merge-requests-holder').css("opacity", '0.5')
|
||||
issues_url = form.attr('action') + '?' + form.serialize()
|
||||
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: form.attr('action')
|
||||
data: form.serialize()
|
||||
complete: ->
|
||||
$('.merge-requests-holder').css("opacity", '1.0')
|
||||
success: (data) ->
|
||||
$('.merge-requests-holder').html(data.html)
|
||||
# Change url so if user reload a page - search results are saved
|
||||
history.replaceState {page: issues_url}, document.title, issues_url
|
||||
MergeRequests.reload()
|
||||
dataType: "json"
|
||||
|
||||
reload: ->
|
||||
$('#filter_issue_search').val($('#issue_search').val())
|
||||
@@ -24,7 +24,7 @@ class @MilestoneSelect
|
||||
|
||||
if issueUpdateURL
|
||||
milestoneLinkTemplate = _.template(
|
||||
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= title %></a>'
|
||||
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>'
|
||||
)
|
||||
|
||||
milestoneLinkNoneTemplate = '<div class="light">None</div>'
|
||||
@@ -71,7 +71,7 @@ class @MilestoneSelect
|
||||
defaultLabel
|
||||
fieldName: $dropdown.data('field-name')
|
||||
text: (milestone) ->
|
||||
milestone.title
|
||||
_.escape(milestone.title)
|
||||
id: (milestone) ->
|
||||
if !useId
|
||||
milestone.name
|
||||
@@ -97,7 +97,7 @@ class @MilestoneSelect
|
||||
selectedMilestone = selected.name
|
||||
else
|
||||
selectedMilestone = ''
|
||||
Issues.filterResults $dropdown.closest('form')
|
||||
Issuable.filterResults $dropdown.closest('form')
|
||||
else if $dropdown.hasClass('js-filter-submit')
|
||||
$dropdown.closest('form').submit()
|
||||
else
|
||||
|
||||
@@ -75,6 +75,9 @@ class @Notes
|
||||
# when issue status changes, we need to refresh data
|
||||
$(document).on "issuable:change", @refresh
|
||||
|
||||
# when a key is clicked on the notes
|
||||
$(document).on "keydown", ".js-note-text", @keydownNoteText
|
||||
|
||||
cleanBinding: ->
|
||||
$(document).off "ajax:success", ".js-main-target-form"
|
||||
$(document).off "ajax:success", ".js-discussion-note-form"
|
||||
@@ -92,10 +95,19 @@ class @Notes
|
||||
$(document).off "click", ".js-note-target-reopen"
|
||||
$(document).off "click", ".js-note-target-close"
|
||||
$(document).off "click", ".js-note-discard"
|
||||
$(document).off "keydown", ".js-note-text"
|
||||
|
||||
$('.note .js-task-list-container').taskList('disable')
|
||||
$(document).off 'tasklist:changed', '.note .js-task-list-container'
|
||||
|
||||
keydownNoteText: (e) ->
|
||||
$this = $(this)
|
||||
if $this.val() is '' and e.which is 38 #aka the up key
|
||||
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
|
||||
if myLastNote.length
|
||||
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
|
||||
myLastNoteEditBtn.trigger('click', [true, myLastNote])
|
||||
|
||||
initRefresh: ->
|
||||
clearInterval(Notes.interval)
|
||||
Notes.interval = setInterval =>
|
||||
@@ -283,32 +295,10 @@ class @Notes
|
||||
show the form
|
||||
###
|
||||
setupNoteForm: (form) ->
|
||||
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
|
||||
form.removeClass "js-new-note-form"
|
||||
form.find('.div-dropzone').remove()
|
||||
|
||||
# hide discard button
|
||||
form.find('.js-note-discard').hide()
|
||||
|
||||
# setup preview buttons
|
||||
previewButton = form.find(".js-md-preview-button")
|
||||
new GLForm form
|
||||
|
||||
textarea = form.find(".js-note-text")
|
||||
|
||||
textarea.on "input", ->
|
||||
if $(this).val().trim() isnt ""
|
||||
previewButton.removeClass("turn-off").addClass "turn-on"
|
||||
else
|
||||
previewButton.removeClass("turn-on").addClass "turn-off"
|
||||
|
||||
textarea.on 'focus', ->
|
||||
$(this).closest('.md-area').addClass 'is-focused'
|
||||
|
||||
textarea.on 'blur', ->
|
||||
$(this).closest('.md-area').removeClass 'is-focused'
|
||||
|
||||
autosize(textarea)
|
||||
|
||||
new Autosave textarea, [
|
||||
"Note"
|
||||
form.find("#note_commit_id").val()
|
||||
@@ -317,11 +307,6 @@ class @Notes
|
||||
form.find("#note_noteable_id").val()
|
||||
]
|
||||
|
||||
# remove notify commit author checkbox for non-commit notes
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new DropzoneInput(form)
|
||||
form.show()
|
||||
|
||||
###
|
||||
Called in response to the new note form being submitted
|
||||
|
||||
@@ -370,39 +355,38 @@ class @Notes
|
||||
Adds a hidden div with the original content of the note to fill the edit note form with
|
||||
if the user cancels
|
||||
###
|
||||
showEditForm: (e) ->
|
||||
showEditForm: (e, scrollTo, myLastNote) ->
|
||||
e.preventDefault()
|
||||
note = $(this).closest(".note")
|
||||
note.addClass "is-editting"
|
||||
form = note.find(".note-edit-form")
|
||||
isNewForm = form.is(':not(.gfm-form)')
|
||||
if isNewForm
|
||||
form.addClass('gfm-form')
|
||||
|
||||
form.addClass('current-note-edit-form')
|
||||
|
||||
# Show the attachment delete link
|
||||
note.find(".js-note-attachment-delete").show()
|
||||
|
||||
# Setup markdown form
|
||||
if isNewForm
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new DropzoneInput(form)
|
||||
done = ($noteText) ->
|
||||
# Neat little trick to put the cursor at the end
|
||||
noteTextVal = $noteText.val()
|
||||
$noteText.val('').val(noteTextVal);
|
||||
|
||||
textarea = form.find("textarea")
|
||||
textarea.focus()
|
||||
|
||||
if isNewForm
|
||||
autosize(textarea)
|
||||
|
||||
# HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?).
|
||||
# The textarea has the correct value, Chrome just won't show it unless we
|
||||
# modify it, so let's clear it and re-set it!
|
||||
value = textarea.val()
|
||||
textarea.val ""
|
||||
textarea.val value
|
||||
|
||||
if isNewForm
|
||||
disableButtonIfEmptyField textarea, form.find(".js-comment-button")
|
||||
new GLForm form
|
||||
if scrollTo? and myLastNote?
|
||||
# scroll to the bottom
|
||||
# so the open of the last element doesn't make a jump
|
||||
$('html, body').scrollTop($(document).height());
|
||||
$('html, body').animate({
|
||||
scrollTop: myLastNote.offset().top - 150
|
||||
}, 500, ->
|
||||
$noteText = form.find(".js-note-text")
|
||||
$noteText.focus()
|
||||
done($noteText)
|
||||
);
|
||||
else
|
||||
$noteText = form.find('.js-note-text')
|
||||
$noteText.focus()
|
||||
done($noteText)
|
||||
|
||||
###
|
||||
Called in response to clicking the edit note link
|
||||
@@ -559,6 +543,9 @@ class @Notes
|
||||
removeDiscussionNoteForm: (form)->
|
||||
row = form.closest("tr")
|
||||
|
||||
glForm = form.data 'gl-form'
|
||||
glForm.destroy()
|
||||
|
||||
form.find(".js-note-text").data("autosave").reset()
|
||||
|
||||
# show the reply button (will only work for replies)
|
||||
@@ -570,7 +557,6 @@ class @Notes
|
||||
# only remove the form
|
||||
form.remove()
|
||||
|
||||
|
||||
cancelDiscussionForm: (e) =>
|
||||
e.preventDefault()
|
||||
form = $(e.target).closest(".js-discussion-note-form")
|
||||
|
||||
@@ -45,9 +45,10 @@ class @Profile
|
||||
|
||||
saveForm: ->
|
||||
self = @
|
||||
|
||||
formData = new FormData(@form[0])
|
||||
formData.append('user[avatar]', @avatarGlCrop.getBlob(), 'avatar.png')
|
||||
|
||||
avatarBlob = @avatarGlCrop.getBlob()
|
||||
formData.append('user[avatar]', avatarBlob, 'avatar.png') if avatarBlob?
|
||||
|
||||
$.ajax
|
||||
url: @form.attr('action')
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
class @Sidebar
|
||||
constructor: (currentUser) ->
|
||||
@sidebar = $('aside')
|
||||
|
||||
@addEventListeners()
|
||||
|
||||
addEventListeners: ->
|
||||
$('aside').on('click', '.sidebar-collapsed-icon', @sidebarCollapseClicked)
|
||||
$('.dropdown').on('hidden.gl.dropdown', @sidebarDropdownHidden)
|
||||
@sidebar.on('click', '.sidebar-collapsed-icon', @, @sidebarCollapseClicked)
|
||||
$('.dropdown').on('hidden.gl.dropdown', @, @onSidebarDropdownHidden)
|
||||
$('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading)
|
||||
$('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded)
|
||||
|
||||
@@ -30,26 +32,56 @@ class @Sidebar
|
||||
else
|
||||
i.show()
|
||||
|
||||
|
||||
sidebarCollapseClicked: (e) ->
|
||||
sidebar = e.data
|
||||
e.preventDefault()
|
||||
$block = $(@).closest('.block')
|
||||
sidebar.openDropdown($block);
|
||||
|
||||
$('aside')
|
||||
.find('.gutter-toggle')
|
||||
.trigger('click')
|
||||
$editLink = $block.find('.edit-link')
|
||||
openDropdown: (blockOrName) ->
|
||||
$block = if _.isString(blockOrName) then @getBlock(blockOrName) else blockOrName
|
||||
|
||||
if $editLink.length
|
||||
$editLink.trigger('click')
|
||||
$block.addClass('collapse-after-update')
|
||||
$('.page-with-sidebar').addClass('with-overlay')
|
||||
$block.find('.edit-link').trigger('click')
|
||||
|
||||
sidebarDropdownHidden: (e) ->
|
||||
if not @isOpen()
|
||||
@setCollapseAfterUpdate($block)
|
||||
@toggleSidebar('open')
|
||||
|
||||
setCollapseAfterUpdate: ($block) ->
|
||||
$block.addClass('collapse-after-update')
|
||||
$('.page-with-sidebar').addClass('with-overlay')
|
||||
|
||||
onSidebarDropdownHidden: (e) ->
|
||||
sidebar = e.data
|
||||
e.preventDefault()
|
||||
$block = $(@).closest('.block')
|
||||
sidebar.sidebarDropdownHidden($block)
|
||||
|
||||
sidebarDropdownHidden: ($block) ->
|
||||
if $block.hasClass('collapse-after-update')
|
||||
$block.removeClass('collapse-after-update')
|
||||
$('.page-with-sidebar').removeClass('with-overlay')
|
||||
$('aside')
|
||||
.find('.gutter-toggle')
|
||||
.trigger('click')
|
||||
@toggleSidebar('hide')
|
||||
|
||||
triggerOpenSidebar: ->
|
||||
@sidebar
|
||||
.find('.js-sidebar-toggle')
|
||||
.trigger('click')
|
||||
|
||||
toggleSidebar: (action = 'toggle') ->
|
||||
if action is 'toggle'
|
||||
@triggerOpenSidebar()
|
||||
|
||||
if action is 'open'
|
||||
@triggerOpenSidebar() if not @isOpen()
|
||||
|
||||
if action is 'hide'
|
||||
@triggerOpenSidebar() is @isOpen()
|
||||
|
||||
isOpen: ->
|
||||
@sidebar.is('.right-sidebar-expanded')
|
||||
|
||||
getBlock: (name) ->
|
||||
@sidebar.find(".block.#{name}")
|
||||
|
||||
|
||||
|
||||
@@ -2,34 +2,35 @@ class @Shortcuts
|
||||
constructor: ->
|
||||
@enabledHelp = []
|
||||
Mousetrap.reset()
|
||||
Mousetrap.bind('?', @selectiveHelp)
|
||||
Mousetrap.bind('?', @onToggleHelp)
|
||||
Mousetrap.bind('s', Shortcuts.focusSearch)
|
||||
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
|
||||
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
|
||||
|
||||
selectiveHelp: (e) =>
|
||||
Shortcuts.showHelp(e, @enabledHelp)
|
||||
onToggleHelp: (e) =>
|
||||
e.preventDefault()
|
||||
@toggleHelp(@enabledHelp)
|
||||
|
||||
toggleMarkdownPreview: (e) =>
|
||||
$(document).triggerHandler('markdown-preview:toggle', [e])
|
||||
|
||||
@showHelp: (e, location) ->
|
||||
if $('#modal-shortcuts').length > 0
|
||||
$('#modal-shortcuts').modal('show')
|
||||
else
|
||||
url = '/help/shortcuts'
|
||||
url = gon.relative_url_root + url if gon.relative_url_root?
|
||||
$.ajax(
|
||||
url: url,
|
||||
dataType: 'script',
|
||||
success: (e) ->
|
||||
if location and location.length > 0
|
||||
$(l).show() for l in location
|
||||
else
|
||||
$('.hidden-shortcut').show()
|
||||
$('.js-more-help-button').remove()
|
||||
)
|
||||
e.preventDefault()
|
||||
toggleHelp: (location) ->
|
||||
$modal = $('#modal-shortcuts')
|
||||
|
||||
if $modal.length
|
||||
$modal.modal('toggle')
|
||||
return
|
||||
|
||||
$.ajax(
|
||||
url: gon.shortcuts_path,
|
||||
dataType: 'script',
|
||||
success: (e) ->
|
||||
if location and location.length > 0
|
||||
$(l).show() for l in location
|
||||
else
|
||||
$('.hidden-shortcut').show()
|
||||
$('.js-more-help-button').remove()
|
||||
)
|
||||
|
||||
@focusSearch: (e) ->
|
||||
$('#search').focus()
|
||||
|
||||
@@ -4,18 +4,8 @@
|
||||
class @ShortcutsIssuable extends ShortcutsNavigation
|
||||
constructor: (isMergeRequest) ->
|
||||
super()
|
||||
Mousetrap.bind('a', ->
|
||||
$('.block.assignee .edit-link').trigger('click')
|
||||
return false
|
||||
)
|
||||
Mousetrap.bind('m', ->
|
||||
$('.block.milestone .edit-link').trigger('click')
|
||||
return false
|
||||
)
|
||||
Mousetrap.bind('r', =>
|
||||
@replyWithSelectedText()
|
||||
return false
|
||||
)
|
||||
Mousetrap.bind('a', @openSidebarDropdown.bind(@, 'assignee'))
|
||||
Mousetrap.bind('m', @openSidebarDropdown.bind(@, 'milestone'))
|
||||
Mousetrap.bind('j', =>
|
||||
@prevIssue()
|
||||
return false
|
||||
@@ -28,7 +18,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation
|
||||
@editIssue()
|
||||
return false
|
||||
)
|
||||
|
||||
Mousetrap.bind('l', @openSidebarDropdown.bind(@, 'labels'))
|
||||
|
||||
if isMergeRequest
|
||||
@enabledHelp.push('.hidden-shortcut.merge_requests')
|
||||
@@ -71,3 +61,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation
|
||||
editIssue: ->
|
||||
$editBtn = $('.issuable-edit')
|
||||
Turbolinks.visit($editBtn.attr('href'))
|
||||
|
||||
openSidebarDropdown: (name) ->
|
||||
sidebar.openDropdown(name)
|
||||
return false
|
||||
|
||||
@@ -14,6 +14,7 @@ class @ShortcutsNavigation extends Shortcuts
|
||||
Mousetrap.bind('g m', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests'))
|
||||
Mousetrap.bind('g w', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki'))
|
||||
Mousetrap.bind('g s', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-snippets'))
|
||||
Mousetrap.bind('i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-new-issue'))
|
||||
@enabledHelp.push('.hidden-shortcut.project')
|
||||
|
||||
@findAndFollowLink: (selector) ->
|
||||
|
||||
@@ -2,7 +2,7 @@ class @Subscription
|
||||
constructor: (container) ->
|
||||
$container = $(container)
|
||||
@url = $container.attr('data-url')
|
||||
@subscribe_button = $container.find('.subscribe-button')
|
||||
@subscribe_button = $container.find('.js-subscribe-button')
|
||||
@subscription_status = $container.find('.subscription-status')
|
||||
@subscribe_button.unbind('click').click(@toggleSubscription)
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
class @Todos
|
||||
constructor: (@name) ->
|
||||
constructor: (opts = {}) ->
|
||||
{
|
||||
@el = $('.js-todos-options')
|
||||
} = opts
|
||||
|
||||
@perPage = @el.data('perPage')
|
||||
|
||||
@clearListeners()
|
||||
@initBtnListeners()
|
||||
|
||||
@@ -26,6 +32,7 @@ class @Todos
|
||||
dataType: 'json'
|
||||
data: '_method': 'delete'
|
||||
success: (data) =>
|
||||
@redirectIfNeeded data.count
|
||||
@clearDone $this.closest('li')
|
||||
@updateBadges data
|
||||
|
||||
@@ -57,11 +64,46 @@ class @Todos
|
||||
$('.todos-pending .badge, .todos-pending-count').text data.count
|
||||
$('.todos-done .badge').text data.done_count
|
||||
|
||||
getTotalPages: ->
|
||||
@el.data('totalPages')
|
||||
|
||||
getCurrentPage: ->
|
||||
@el.data('currentPage')
|
||||
|
||||
getTodosPerPage: ->
|
||||
@el.data('perPage')
|
||||
|
||||
redirectIfNeeded: (total) ->
|
||||
currPages = @getTotalPages()
|
||||
currPage = @getCurrentPage()
|
||||
|
||||
# Refresh if no remaining Todos
|
||||
if not total
|
||||
location.reload()
|
||||
return
|
||||
|
||||
# Do nothing if no pagination
|
||||
return if not currPages
|
||||
|
||||
newPages = Math.ceil(total / @getTodosPerPage())
|
||||
url = location.href # Includes query strings
|
||||
|
||||
# If new total of pages is different than we have now
|
||||
if newPages isnt currPages
|
||||
# Redirect to previous page if there's one available
|
||||
if currPages > 1 and currPage is currPages
|
||||
pageParams =
|
||||
page: currPages - 1
|
||||
url = gl.utils.mergeUrlParams(pageParams, url)
|
||||
|
||||
Turbolinks.visit(url)
|
||||
|
||||
goToTodoUrl: (e)->
|
||||
todoLink = $(this).data('url')
|
||||
return unless todoLink
|
||||
|
||||
if e.metaKey
|
||||
# Allow Meta-Click or Mouse3-click to open in a new tab
|
||||
if e.metaKey or e.which is 2
|
||||
e.preventDefault()
|
||||
window.open(todoLink,'_blank')
|
||||
else
|
||||
|
||||
@@ -12,6 +12,7 @@ class @UsersSelect
|
||||
showNullUser = $dropdown.data('null-user')
|
||||
showAnyUser = $dropdown.data('any-user')
|
||||
firstUser = $dropdown.data('first-user')
|
||||
@authorId = $dropdown.data('author-id')
|
||||
selectedId = $dropdown.data('selected')
|
||||
defaultLabel = $dropdown.data('default-label')
|
||||
issueURL = $dropdown.data('issueUpdate')
|
||||
@@ -157,7 +158,7 @@ class @UsersSelect
|
||||
|
||||
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
|
||||
selectedId = user.id
|
||||
Issues.filterResults $dropdown.closest('form')
|
||||
Issuable.filterResults $dropdown.closest('form')
|
||||
else if $dropdown.hasClass 'js-filter-submit'
|
||||
$dropdown.closest('form').submit()
|
||||
else
|
||||
@@ -207,6 +208,7 @@ class @UsersSelect
|
||||
@projectId = $(select).data('project-id')
|
||||
@groupId = $(select).data('group-id')
|
||||
@showCurrentUser = $(select).data('current-user')
|
||||
@authorId = $(select).data('author-id')
|
||||
showNullUser = $(select).data('null-user')
|
||||
showAnyUser = $(select).data('any-user')
|
||||
showEmailUser = $(select).data('email-user')
|
||||
@@ -312,6 +314,7 @@ class @UsersSelect
|
||||
project_id: @projectId
|
||||
group_id: @groupId
|
||||
current_user: @showCurrentUser
|
||||
author_id: @authorId
|
||||
dataType: "json"
|
||||
).done (users) ->
|
||||
callback(users)
|
||||
|
||||
@@ -144,6 +144,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
.btn-transparent {
|
||||
color: $btn-transparent-color;
|
||||
background-color: transparent;
|
||||
|
||||
@@ -54,6 +54,10 @@
|
||||
fill: #254e77 !important;
|
||||
}
|
||||
|
||||
.future {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.domain-background {
|
||||
fill: none;
|
||||
shape-rendering: crispedges;
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
|
||||
.dropdown-title {
|
||||
position: relative;
|
||||
padding: 0 25px 15px;
|
||||
padding: 0 25px 10px;
|
||||
margin: 0 10px 10px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
@@ -278,7 +278,7 @@
|
||||
right: 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: -1px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.dropdown-menu-back {
|
||||
@@ -320,7 +320,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-input-field {
|
||||
.dropdown-input-field, .default-dropdown-input {
|
||||
width: 100%;
|
||||
padding: 0 7px;
|
||||
color: $dropdown-input-color;
|
||||
@@ -358,6 +358,13 @@
|
||||
border-top: 1px solid $dropdown-divider-color;
|
||||
}
|
||||
|
||||
.dropdown-due-date-footer {
|
||||
padding-top: 0;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.dropdown-footer-list {
|
||||
font-size: 14px;
|
||||
|
||||
@@ -395,3 +402,122 @@
|
||||
height: 15px;
|
||||
border-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
.dropdown-menu-due-date {
|
||||
.dropdown-content {
|
||||
max-height: 230px;
|
||||
}
|
||||
|
||||
.ui-widget {
|
||||
table {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.ui-datepicker-inline {
|
||||
padding: 0 10px;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ui-datepicker-header {
|
||||
padding: 0 8px 10px;
|
||||
border: 0;
|
||||
|
||||
.ui-icon {
|
||||
background: none;
|
||||
font-size: 20px;
|
||||
text-indent: 0;
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
color: $dropdown-title-btn-color;
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-state-active,
|
||||
.ui-state-hover {
|
||||
color: $md-link-color;
|
||||
background-color: $calendar-hover-bg;
|
||||
}
|
||||
|
||||
.ui-datepicker-prev,
|
||||
.ui-datepicker-next {
|
||||
top: 0;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
|
||||
.ui-icon:before {
|
||||
color: $md-link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-prev {
|
||||
left: 0;
|
||||
|
||||
.ui-icon:before {
|
||||
content: '\f104';
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-next {
|
||||
right: 0;
|
||||
|
||||
.ui-icon:before {
|
||||
content: '\f105';
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0;
|
||||
border: 1px solid $calendar-border-color;
|
||||
|
||||
&:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
line-height: 17px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-title {
|
||||
color: $gl-gray;
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 2px 0;
|
||||
color: $calendar-header-color;
|
||||
font-weight: normal;
|
||||
text-transform: lowercase;
|
||||
border-top: 1px solid $calendar-border-color;
|
||||
}
|
||||
|
||||
.ui-datepicker-unselectable {
|
||||
background-color: $calendar-unselectable-bg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,14 @@
|
||||
|
||||
.filename {
|
||||
&.old {
|
||||
display: inline-block;
|
||||
span.idiff {
|
||||
background-color: #f8cbcb;
|
||||
}
|
||||
}
|
||||
|
||||
&.new {
|
||||
display: inline-block;
|
||||
span.idiff {
|
||||
background-color: #a6f3a6;
|
||||
}
|
||||
@@ -82,10 +84,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.blob_file {
|
||||
|
||||
}
|
||||
|
||||
&.blob-no-preview {
|
||||
background: #eee;
|
||||
text-shadow: 0 1px 2px #fff;
|
||||
@@ -129,6 +127,11 @@
|
||||
td.line-numbers {
|
||||
float: none;
|
||||
border-left: 1px solid #ddd;
|
||||
|
||||
i {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
td.lines {
|
||||
padding: 0;
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
/**
|
||||
* Styles that apply to all GFM related forms.
|
||||
*/
|
||||
.issue-form, .merge-request-form, .wiki-form {
|
||||
.description {
|
||||
height: 16em;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-form {
|
||||
.description {
|
||||
height: 26em;
|
||||
}
|
||||
}
|
||||
|
||||
.milestone-form {
|
||||
.description {
|
||||
height: 14em;
|
||||
}
|
||||
}
|
||||
|
||||
.gfm-commit, .gfm-commit_range {
|
||||
font-family: $monospace_font;
|
||||
|
||||
@@ -71,7 +71,7 @@ header {
|
||||
.header-content {
|
||||
position: relative;
|
||||
height: $header-height;
|
||||
padding-right: 20px;
|
||||
padding-right: 40px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-right: 0;
|
||||
@@ -122,6 +122,10 @@ header {
|
||||
}
|
||||
}
|
||||
|
||||
.project-item-select-holder {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.impersonation i {
|
||||
color: $red-normal;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
.status-box {
|
||||
|
||||
|
||||
/* Extra small devices (phones, less than 768px) */
|
||||
/* No media query since this is the default in Bootstrap */
|
||||
padding: 5px 11px;
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
.div-dropzone-wrapper {
|
||||
.div-dropzone {
|
||||
position: relative;
|
||||
margin-bottom: -5px;
|
||||
|
||||
.div-dropzone-focus {
|
||||
border-color: #66afe9 !important;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6) !important;
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
.div-dropzone-hover {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -0.5em;
|
||||
margin-left: -0.6em;
|
||||
margin-top: -11.5px;
|
||||
margin-left: -15px;
|
||||
opacity: 0;
|
||||
font-size: 50px;
|
||||
font-size: 30px;
|
||||
transition: opacity 200ms ease-in-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -97,3 +90,12 @@
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.md {
|
||||
&.md-preview-holder {
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
word-break: keep-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,13 +70,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.issue-details {
|
||||
.creator,
|
||||
.page-title .btn-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
%ul.notes .note-role, .note-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -121,9 +121,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.select2-container-multi .select2-choices .select2-search-choice {
|
||||
}
|
||||
|
||||
.select2-drop-active {
|
||||
margin-top: 6px;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
border-bottom: 1px solid $border-white-light;
|
||||
|
||||
&:target {
|
||||
background: $row-hover;
|
||||
background: $line-target-blue;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
@@ -39,8 +39,7 @@
|
||||
.diff-file {
|
||||
border: 1px solid $border-color;
|
||||
border-bottom: none;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
@import "bootstrap/modals";
|
||||
@import "bootstrap/tooltip";
|
||||
@import "bootstrap/popovers";
|
||||
@import "bootstrap/carousel";
|
||||
|
||||
// Utility classes
|
||||
.clearfix {
|
||||
|
||||
@@ -250,14 +250,6 @@ a > code {
|
||||
* Textareas intended for GFM
|
||||
*
|
||||
*/
|
||||
.js-gfm-input {
|
||||
font-family: $monospace_font;
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.md-preview {
|
||||
}
|
||||
|
||||
.strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ $light-grey-header: #faf9f9;
|
||||
*/
|
||||
$gl-primary: $blue-normal;
|
||||
$gl-success: $green-normal;
|
||||
$gl-success-focus: rgba($gl-success, .4);
|
||||
$gl-info: $blue-normal;
|
||||
$gl-warning: $orange-normal;
|
||||
$gl-danger: $red-normal;
|
||||
@@ -167,8 +168,12 @@ $line-removed: #fbe9eb;
|
||||
$line-removed-dark: #fac5cd;
|
||||
$line-number-old: #f9d7dc;
|
||||
$line-number-new: #ddfbe6;
|
||||
$line-number-select: #fbf2da;
|
||||
$match-line: #fafafa;
|
||||
$table-border-gray: #f0f0f0;
|
||||
$line-target-blue: #eaf3fc;
|
||||
$line-select-yellow: #fcf8e7;
|
||||
$line-select-yellow-dark: #f0e2bd;
|
||||
/*
|
||||
* Fonts
|
||||
*/
|
||||
@@ -240,3 +245,8 @@ $note-form-border-color: #e5e5e5;
|
||||
$note-toolbar-color: #959494;
|
||||
|
||||
$zen-control-hover-color: #111;
|
||||
|
||||
$calendar-header-color: #b8b8b8;
|
||||
$calendar-hover-bg: #ecf3fe;
|
||||
$calendar-border-color: rgba(#000, .1);
|
||||
$calendar-unselectable-bg: #faf9f9;
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #557;
|
||||
border-color: darken(#557, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.2), #808080);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #49483e;
|
||||
border-color: darken(#49483e, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(166, 226, 46, 0.1), rgba(166, 226, 46, 0.15), #808080);
|
||||
}
|
||||
@@ -105,8 +111,6 @@
|
||||
.vg { color: #f8f8f2 } /* Name.Variable.Global */
|
||||
.vi { color: #f8f8f2 } /* Name.Variable.Instance */
|
||||
.il { color: #ae81ff } /* Literal.Number.Integer.Long */
|
||||
|
||||
.gh { } /* Generic Heading & Diff Header */
|
||||
.gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
|
||||
.gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
|
||||
.gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #174652;
|
||||
border-color: darken(#174652, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(133, 153, 0, 0.15), rgba(133, 153, 0, 0.25), #113b46);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #ddd8c5;
|
||||
border-color: darken(#ddd8c5, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.25), #c5d0d4);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
|
||||
.diff-line-num {
|
||||
&.old {
|
||||
background-color: $line-number-old;
|
||||
@@ -31,11 +32,16 @@
|
||||
background-color: $line-number-new;
|
||||
border-color: $line-added-dark;
|
||||
}
|
||||
|
||||
&.hll:not(.empty-cell) {
|
||||
background-color: $line-number-select;
|
||||
border-color: $line-select-yellow-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.line_content {
|
||||
&.old {
|
||||
background: $line-removed;
|
||||
background-color: $line-removed;
|
||||
|
||||
span.idiff {
|
||||
background-color: $line-removed-dark;
|
||||
@@ -52,7 +58,11 @@
|
||||
|
||||
&.match {
|
||||
color: $black-transparent;
|
||||
background: $match-line;
|
||||
background-color: $match-line;
|
||||
}
|
||||
|
||||
&.hll:not(.empty-cell) {
|
||||
background-color: $line-select-yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@ li.commit {
|
||||
}
|
||||
}
|
||||
|
||||
.item-title {
|
||||
display: inline-block;
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.commit-row-description {
|
||||
font-size: 14px;
|
||||
border-left: 1px solid #eee;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.well-confirmation {
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
> h1 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.lead {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.confirmation-content {
|
||||
a {
|
||||
color: $md-link-color;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
.detail-page-header {
|
||||
padding: 11px 0;
|
||||
padding: $gl-padding-top 0;
|
||||
border-bottom: 1px solid $border-color;
|
||||
color: #5c5d5e;
|
||||
font-size: 16px;
|
||||
@@ -16,11 +16,6 @@
|
||||
.issue_created_ago, .author_link {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.issue-meta {
|
||||
display: inline-block;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-page-description {
|
||||
@@ -41,4 +36,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wiki {
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
word-break: keep-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border-radius: 0 0 3px 3px;
|
||||
-webkit-overflow-scrolling: auto;
|
||||
|
||||
.unfold {
|
||||
cursor: pointer;
|
||||
@@ -67,8 +68,26 @@
|
||||
line-height: $code_line_height;
|
||||
font-size: $code_font_size;
|
||||
|
||||
&.noteable_line {
|
||||
position: relative;
|
||||
|
||||
&.old {
|
||||
&:before {
|
||||
content: '-';
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
&.new {
|
||||
&:before {
|
||||
content: '+';
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,7 +336,7 @@
|
||||
}
|
||||
|
||||
.diff-file .line_content {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.diff-wrap-lines .line_content {
|
||||
@@ -391,3 +410,23 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.file-holder {
|
||||
.diff-line-num:not(.js-unfold-bottom) {
|
||||
a {
|
||||
&:before {
|
||||
content: attr(data-linenumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.discussion {
|
||||
.diff-content {
|
||||
.diff-line-num {
|
||||
&:before {
|
||||
content: attr(data-linenumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
line-height: 42px;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
|
||||
.pull-right {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-ref {
|
||||
@@ -53,4 +57,9 @@
|
||||
.select2 {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.encoding-selector,
|
||||
.license-selector {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
}
|
||||
|
||||
.graphs {
|
||||
.graph-author-commits-count {
|
||||
}
|
||||
|
||||
.graph-author-email {
|
||||
float: right;
|
||||
color: #777;
|
||||
|
||||
@@ -59,8 +59,10 @@
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
|
||||
.form-actions {
|
||||
margin: -$gl-padding+1;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -16,3 +16,24 @@ i.icon-gitorious-big {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.import-jobs-from-col,
|
||||
.import-jobs-to-col {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.import-jobs-status-col {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.btn-import {
|
||||
.loading-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
.loading-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
top: 58px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
transition: width .3s;
|
||||
background: $gray-light;
|
||||
padding: 10px 20px;
|
||||
@@ -173,12 +174,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.subscribe-button {
|
||||
span {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.right-sidebar-collapsed {
|
||||
/* Extra small devices (phones, less than 768px) */
|
||||
display: none;
|
||||
@@ -247,16 +242,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
.issuable-pager {
|
||||
background: $gray-normal;
|
||||
border: 1px solid $border-gray-normal;
|
||||
&:hover {
|
||||
background: $gray-dark;
|
||||
border: 1px solid $border-gray-dark;
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
@extend .btn-primary
|
||||
}
|
||||
}
|
||||
|
||||
a:not(.btn) {
|
||||
a:not(.issuable-pager) {
|
||||
&:hover {
|
||||
color: $md-link-color;
|
||||
text-decoration: none;
|
||||
@@ -279,10 +278,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-default.gutter-toggle {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.detail-page-description {
|
||||
small {
|
||||
color: $gray-darkest;
|
||||
@@ -322,3 +317,56 @@
|
||||
color: #8c8c8c;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-form-padding-top {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-status-box {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-header {
|
||||
position: relative;
|
||||
padding-left: 45px;
|
||||
padding-right: 45px;
|
||||
line-height: 35px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
float: left;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-actions {
|
||||
padding-top: 10px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
float: right;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-gutter-toggle {
|
||||
@media (max-width: $screen-sm-max) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-meta {
|
||||
display: inline-block;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
@@ -86,41 +86,9 @@ form.edit-issue {
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.issue-btn-group {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
|
||||
.btn-group {
|
||||
width: 100%;
|
||||
|
||||
ul {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
|
||||
&:first-child:not(:last-child) {
|
||||
|
||||
}
|
||||
|
||||
&:not(:first-child):not(:last-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&:last-child:not(:first-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.issue {
|
||||
&:hover .issue-actions {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.issue-updated-at {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,11 +101,3 @@ form.edit-issue {
|
||||
color: $gl-text-color;
|
||||
margin-left: 52px;
|
||||
}
|
||||
|
||||
.editor-details {
|
||||
display: block;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,19 +79,30 @@
|
||||
color: $white-light;
|
||||
}
|
||||
|
||||
@mixin labels-mobile {
|
||||
@media (max-width: $screen-xs-min) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.manage-labels-list {
|
||||
|
||||
.prepend-left-10 {
|
||||
.prepend-left-10, .prepend-description-left {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
vertical-align: middle;
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
@include labels-mobile;
|
||||
}
|
||||
|
||||
.prepend-description-left {
|
||||
width: 57%;
|
||||
|
||||
@include labels-mobile;
|
||||
}
|
||||
|
||||
.pull-info-right {
|
||||
@@ -106,7 +117,7 @@
|
||||
padding: 6px;
|
||||
color: $gl-text-color;
|
||||
|
||||
&.subscribe-button {
|
||||
&.label-subscribe-button {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
}
|
||||
|
||||
.note-textarea {
|
||||
display: block;
|
||||
padding: 10px 0;
|
||||
color: $gl-gray;
|
||||
font-family: $regular_font;
|
||||
border: 0;
|
||||
|
||||
@@ -60,11 +62,11 @@
|
||||
padding: $gl-padding-top $gl-padding;
|
||||
border: 1px solid $note-form-border-color;
|
||||
border-radius: $border-radius-base;
|
||||
transition: border-color ease-in-out 0.15s,
|
||||
box-shadow ease-in-out 0.15s;
|
||||
|
||||
&.is-focused {
|
||||
border-color: $focus-border-color;
|
||||
box-shadow: 0 0 2px rgba(#000, .2),
|
||||
0 0 4px rgba($focus-border-color, .4);
|
||||
@extend .form-control:focus;
|
||||
|
||||
.comment-toolbar,
|
||||
.nav-links {
|
||||
@@ -72,15 +74,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
code {
|
||||
white-space: normal;
|
||||
}
|
||||
&.is-dropzone-hover {
|
||||
border-color: $gl-success;
|
||||
box-shadow: 0 0 2px $black-transparent,
|
||||
0 0 4px $gl-success-focus;
|
||||
|
||||
pre {
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
.comment-toolbar,
|
||||
.nav-links {
|
||||
border-color: $gl-success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,16 +81,8 @@ ul.notes {
|
||||
@include md-typography;
|
||||
|
||||
// On diffs code should wrap nicely and not overflow
|
||||
p {
|
||||
code {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
pre {
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
// Reset ul style types since we're nested inside a ul already
|
||||
@@ -117,6 +109,10 @@ ul.notes {
|
||||
border-color: darken(#f5f5f5, 8%);
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
code {
|
||||
word-break: keep-all;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -137,7 +133,7 @@ ul.notes {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.line_content {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +187,9 @@ ul.notes {
|
||||
}
|
||||
}
|
||||
|
||||
.author_link {
|
||||
color: $gl-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.note-headline-light,
|
||||
@@ -198,6 +197,12 @@ ul.notes {
|
||||
color: $notes-light-color;
|
||||
}
|
||||
|
||||
.discussion-headline-light {
|
||||
a {
|
||||
color: $gl-link-color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions for Discussions/Notes
|
||||
*/
|
||||
@@ -209,6 +214,17 @@ ul.notes {
|
||||
color: $notes-action-color;
|
||||
}
|
||||
|
||||
.discussion-actions {
|
||||
@media (max-width: $screen-sm-max) {
|
||||
float: none;
|
||||
margin-left: 0;
|
||||
|
||||
.note-action-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.note-action-button,
|
||||
.discussion-action-button {
|
||||
display: inline-block;
|
||||
@@ -276,8 +292,7 @@ ul.notes {
|
||||
|
||||
.diff-file tr.line_holder {
|
||||
@mixin show-add-diff-note {
|
||||
filter: alpha(opacity=100);
|
||||
opacity: 1.0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.add-diff-note {
|
||||
@@ -287,17 +302,12 @@ ul.notes {
|
||||
padding: 4px;
|
||||
font-size: 16px;
|
||||
color: $gl-link-color;
|
||||
margin-left: -60px;
|
||||
margin-left: -56px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 32px;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
|
||||
// "hide" it by default
|
||||
opacity: 0.0;
|
||||
filter: alpha(opacity=0);
|
||||
|
||||
display: none;
|
||||
&:hover {
|
||||
background: $gl-info;
|
||||
color: #fff;
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
/* Generic print styles */
|
||||
header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
|
||||
.profiler-results {display: none;}
|
||||
|
||||
/* Styles targeted specifically at printing files */
|
||||
.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
|
||||
.file-title {display: none;}
|
||||
.file-holder {border: none;}
|
||||
|
||||
.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
|
||||
.wiki h1 {font-size: 30px;}
|
||||
.wiki h2 {font-size: 22px;}
|
||||
.wiki h3 {font-size: 18px; font-weight: bold; }
|
||||
|
||||
.sidebar-wrapper { display: none; }
|
||||
.nav { display: none; }
|
||||
.btn { display: none; }
|
||||
header,
|
||||
nav,
|
||||
nav.main-nav,
|
||||
nav.navbar-collapse,
|
||||
nav.navbar-collapse.collapse,
|
||||
.profiler-results,
|
||||
.tree-ref-holder,
|
||||
.tree-holder .breadcrumb,
|
||||
.blob-commit-info,
|
||||
.file-title,
|
||||
.file-holder,
|
||||
.sidebar-wrapper,
|
||||
.nav,
|
||||
.btn,
|
||||
ul.notes-form,
|
||||
.merge-request-ci-status .ci-status-link:after,
|
||||
.issuable-gutter-toggle,
|
||||
.gutter-toggle,
|
||||
.issuable-details .content-block-small,
|
||||
.edit-link,
|
||||
.note-action-button {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
.page-gutter {
|
||||
padding-top: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.right-sidebar {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||
redirect_to admin_runners_path
|
||||
end
|
||||
|
||||
def clear_repository_check_states
|
||||
RepositoryCheck::ClearWorker.perform_async
|
||||
|
||||
redirect_to(
|
||||
admin_application_settings_path,
|
||||
notice: 'Started asynchronous removal of all repository check states.'
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_application_setting
|
||||
@@ -66,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||
:admin_notification_email,
|
||||
:user_oauth_applications,
|
||||
:shared_runners_enabled,
|
||||
:shared_runners_text,
|
||||
:max_artifacts_size,
|
||||
:metrics_enabled,
|
||||
:metrics_host,
|
||||
@@ -82,6 +92,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||
:akismet_enabled,
|
||||
:akismet_api_key,
|
||||
:email_author_in_body,
|
||||
:repository_checks_enabled,
|
||||
:metrics_packet_size,
|
||||
restricted_visibility_levels: [],
|
||||
import_sources: []
|
||||
)
|
||||
|
||||
@@ -39,6 +39,6 @@ class Admin::HooksController < Admin::ApplicationController
|
||||
end
|
||||
|
||||
def hook_params
|
||||
params.require(:hook).permit(:url, :enable_ssl_verification)
|
||||
params.require(:hook).permit(:url, :enable_ssl_verification, :push_events, :tag_push_events)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Admin::ProjectsController < Admin::ApplicationController
|
||||
before_action :project, only: [:show, :transfer]
|
||||
before_action :project, only: [:show, :transfer, :repository_check]
|
||||
before_action :group, only: [:show, :transfer]
|
||||
|
||||
def index
|
||||
@@ -8,6 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController
|
||||
@projects = @projects.where("projects.visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
|
||||
@projects = @projects.with_push if params[:with_push].present?
|
||||
@projects = @projects.abandoned if params[:abandoned].present?
|
||||
@projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present?
|
||||
@projects = @projects.non_archived unless params[:with_archived].present?
|
||||
@projects = @projects.search(params[:name]) if params[:name].present?
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@@ -30,6 +31,15 @@ class Admin::ProjectsController < Admin::ApplicationController
|
||||
redirect_to admin_namespace_project_path(@project.namespace, @project)
|
||||
end
|
||||
|
||||
def repository_check
|
||||
RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id)
|
||||
|
||||
redirect_to(
|
||||
admin_namespace_project_path(@project.namespace, @project),
|
||||
notice: 'Repository check was triggered.'
|
||||
)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def project
|
||||
|
||||
@@ -3,6 +3,7 @@ require 'fogbugz'
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
include Gitlab::CurrentSettings
|
||||
include Gitlab::GonHelper
|
||||
include GitlabRoutingHelper
|
||||
include PageLayoutHelper
|
||||
|
||||
@@ -13,7 +14,7 @@ class ApplicationController < ActionController::Base
|
||||
before_action :check_password_expiration
|
||||
before_action :check_2fa_requirement
|
||||
before_action :ldap_security_check
|
||||
before_action :sentry_user_context
|
||||
before_action :sentry_context
|
||||
before_action :default_headers
|
||||
before_action :add_gon_variables
|
||||
before_action :configure_permitted_parameters, if: :devise_controller?
|
||||
@@ -40,13 +41,15 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
protected
|
||||
|
||||
def sentry_user_context
|
||||
if Rails.env.production? && current_application_settings.sentry_enabled && current_user
|
||||
Raven.user_context(
|
||||
id: current_user.id,
|
||||
email: current_user.email,
|
||||
username: current_user.username,
|
||||
)
|
||||
def sentry_context
|
||||
if Rails.env.production? && current_application_settings.sentry_enabled
|
||||
if current_user
|
||||
Raven.user_context(
|
||||
id: current_user.id,
|
||||
email: current_user.email,
|
||||
username: current_user.username,
|
||||
)
|
||||
end
|
||||
|
||||
Raven.tags_context(program: sentry_program_context)
|
||||
end
|
||||
@@ -158,20 +161,6 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
end
|
||||
|
||||
def add_gon_variables
|
||||
gon.api_version = API::API.version
|
||||
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
|
||||
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
|
||||
gon.max_file_size = current_application_settings.max_attachment_size
|
||||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
|
||||
if current_user
|
||||
gon.current_user_id = current_user.id
|
||||
gon.api_token = current_user.private_token
|
||||
end
|
||||
end
|
||||
|
||||
def validate_user_service_ticket!
|
||||
return unless signed_in? && session[:service_tickets]
|
||||
|
||||
|
||||
@@ -12,8 +12,15 @@ class AutocompleteController < ApplicationController
|
||||
if params[:search].blank?
|
||||
# Include current user if available to filter by "Me"
|
||||
if params[:current_user] && current_user
|
||||
@users = [*@users, current_user].uniq
|
||||
@users = [*@users, current_user]
|
||||
end
|
||||
|
||||
if params[:author_id].present?
|
||||
author = User.find_by_id(params[:author_id])
|
||||
@users = [author, *@users] if author
|
||||
end
|
||||
|
||||
@users.uniq!
|
||||
end
|
||||
|
||||
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
|
||||
|
||||
@@ -10,6 +10,8 @@ module FilterProjects
|
||||
def filter_projects(projects)
|
||||
projects = projects.search(params[:filter_projects]) if params[:filter_projects].present?
|
||||
projects = projects.non_archived if params[:archived].blank?
|
||||
projects = projects.personal(current_user) if params[:personal].present? && current_user
|
||||
|
||||
projects
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
class ConfirmationsController < Devise::ConfirmationsController
|
||||
|
||||
def almost_there
|
||||
flash[:notice] = nil
|
||||
render layout: "devise_empty"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def after_resending_confirmation_instructions_path_for(resource)
|
||||
users_almost_there_path
|
||||
end
|
||||
|
||||
def after_confirmation_path_for(resource_name, resource)
|
||||
if signed_in?(resource_name)
|
||||
after_sign_in_path_for(resource)
|
||||
|
||||
@@ -40,6 +40,7 @@ class GroupsController < Groups::ApplicationController
|
||||
@last_push = current_user.recent_push if current_user
|
||||
|
||||
@projects = @projects.includes(:namespace)
|
||||
@projects = @projects.sorted_by_activity
|
||||
@projects = filter_projects(@projects)
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@projects = @projects.page(params[:page]) if params[:filter_projects].blank?
|
||||
|
||||
@@ -51,6 +51,7 @@ class HelpController < ApplicationController
|
||||
end
|
||||
|
||||
def ui
|
||||
@user = User.new(id: 0, name: 'John Doe', username: '@johndoe')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
||||
include Gitlab::CurrentSettings
|
||||
include Gitlab::GonHelper
|
||||
include PageLayoutHelper
|
||||
|
||||
before_action :verify_user_oauth_applications_enabled
|
||||
before_action :authenticate_user!
|
||||
before_action :add_gon_variables
|
||||
|
||||
layout 'profile'
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ class Profiles::KeysController < Profiles::ApplicationController
|
||||
@key = current_user.keys.find(params[:id])
|
||||
end
|
||||
|
||||
# Back-compat: We need to support this URL since git-annex webapp points to it
|
||||
def new
|
||||
redirect_to profile_keys_path
|
||||
end
|
||||
|
||||
def create
|
||||
@key = current_user.keys.new(key_params)
|
||||
|
||||
|
||||
@@ -83,8 +83,7 @@ class Projects::ApplicationController < ApplicationController
|
||||
end
|
||||
|
||||
def apply_diff_view_cookie!
|
||||
view = params[:view] || cookies[:diff_view]
|
||||
cookies.permanent[:diff_view] = params[:view] = view if view
|
||||
cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present?
|
||||
end
|
||||
|
||||
def builds_enabled
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class Projects::BuildsController < Projects::ApplicationController
|
||||
before_action :build, except: [:index, :cancel_all]
|
||||
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
|
||||
before_action :authorize_update_build!, except: [:index, :show, :status]
|
||||
before_action :authorize_update_build!, except: [:index, :show, :status, :raw]
|
||||
layout 'project'
|
||||
|
||||
def index
|
||||
@@ -62,6 +62,14 @@ class Projects::BuildsController < Projects::ApplicationController
|
||||
notice: "Build has been sucessfully erased!"
|
||||
end
|
||||
|
||||
def raw
|
||||
if @build.has_trace?
|
||||
send_file @build.path_to_trace, type: 'text/plain; charset=utf-8', disposition: 'inline'
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build
|
||||
|
||||
@@ -12,7 +12,7 @@ class Projects::CommitController < Projects::ApplicationController
|
||||
before_action :authorize_read_commit_status!, only: [:builds]
|
||||
before_action :commit
|
||||
before_action :define_show_vars, only: [:show, :builds]
|
||||
before_action :authorize_edit_tree!, only: [:revert]
|
||||
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
|
||||
|
||||
def show
|
||||
apply_diff_view_cookie!
|
||||
@@ -60,27 +60,32 @@ class Projects::CommitController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def revert
|
||||
assign_revert_commit_vars
|
||||
assign_change_commit_vars(@commit.revert_branch_name)
|
||||
|
||||
return render_404 if @target_branch.blank?
|
||||
|
||||
create_commit(Commits::RevertService, success_notice: "The #{revert_type_title} has been successfully reverted.",
|
||||
success_path: successful_revert_path, failure_path: failed_revert_path)
|
||||
create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title} has been successfully reverted.",
|
||||
success_path: successful_change_path, failure_path: failed_change_path)
|
||||
end
|
||||
|
||||
def cherry_pick
|
||||
assign_change_commit_vars(@commit.cherry_pick_branch_name)
|
||||
|
||||
return render_404 if @target_branch.blank?
|
||||
|
||||
create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title} has been successfully cherry-picked.",
|
||||
success_path: successful_change_path, failure_path: failed_change_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def revert_type_title
|
||||
@commit.merged_merge_request ? 'merge request' : 'commit'
|
||||
end
|
||||
|
||||
def successful_revert_path
|
||||
def successful_change_path
|
||||
return referenced_merge_request_url if @commit.merged_merge_request
|
||||
|
||||
namespace_project_commits_url(@project.namespace, @project, @target_branch)
|
||||
end
|
||||
|
||||
def failed_revert_path
|
||||
def failed_change_path
|
||||
return referenced_merge_request_url if @commit.merged_merge_request
|
||||
|
||||
namespace_project_commit_url(@project.namespace, @project, params[:id])
|
||||
@@ -116,14 +121,13 @@ class Projects::CommitController < Projects::ApplicationController
|
||||
@builds = Ci::Build.where(commit: ci_commits)
|
||||
end
|
||||
|
||||
def assign_revert_commit_vars
|
||||
def assign_change_commit_vars(mr_source_branch)
|
||||
@commit = project.commit(params[:id])
|
||||
@target_branch = params[:target_branch]
|
||||
@mr_source_branch = @commit.revert_branch_name
|
||||
@mr_source_branch = mr_source_branch
|
||||
@mr_target_branch = @target_branch
|
||||
@commit_params = {
|
||||
commit: @commit,
|
||||
revert_type_title: revert_type_title,
|
||||
create_merge_request: params[:create_merge_request].present? || different_project?
|
||||
}
|
||||
end
|
||||
|
||||
@@ -7,10 +7,12 @@ class Projects::GroupLinksController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
link = project.project_group_links.new
|
||||
link.group_id = params[:link_group_id]
|
||||
link.group_access = params[:link_group_access]
|
||||
link.save
|
||||
group = Group.find(params[:link_group_id])
|
||||
return render_404 unless can?(current_user, :read_group, group)
|
||||
|
||||
project.project_group_links.create(
|
||||
group: group, group_access: params[:link_group_access]
|
||||
)
|
||||
|
||||
redirect_to namespace_project_group_links_path(project.namespace, project)
|
||||
end
|
||||
|
||||
@@ -3,7 +3,8 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
include IssuableActions
|
||||
|
||||
before_action :module_enabled
|
||||
before_action :issue, only: [:edit, :update, :show]
|
||||
before_action :issue,
|
||||
only: [:edit, :update, :show, :referenced_merge_requests, :related_branches]
|
||||
|
||||
# Allow read any issue
|
||||
before_action :authorize_read_issue!, only: [:show]
|
||||
@@ -17,9 +18,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
# Allow issues bulk update
|
||||
before_action :authorize_admin_issues!, only: [:bulk_update]
|
||||
|
||||
# Cross-reference merge requests
|
||||
before_action :closed_by_merge_requests, only: [:show]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
@@ -35,14 +33,15 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
@issues = @issues.page(params[:page])
|
||||
@label = @project.labels.find_by(title: params[:label_name])
|
||||
@labels = @project.labels.where(title: params[:label_name])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.atom { render layout: false }
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("projects/issues/_issues")
|
||||
html: view_to_html_string("projects/issues/_issues"),
|
||||
labels: @labels.as_json(methods: :text_color)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -62,11 +61,9 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def show
|
||||
@note = @project.notes.new(noteable: @issue)
|
||||
@notes = @issue.notes.nonawards.with_associations.fresh
|
||||
@note = @project.notes.new(noteable: @issue)
|
||||
@notes = @issue.notes.nonawards.with_associations.fresh
|
||||
@noteable = @issue
|
||||
@merge_requests = @issue.referenced_merge_requests(current_user)
|
||||
@related_branches = @issue.related_branches - @merge_requests.map(&:source_branch)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
@@ -104,7 +101,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html do
|
||||
if @issue.valid?
|
||||
redirect_to issue_path(@issue)
|
||||
@@ -113,7 +109,32 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
end
|
||||
format.json do
|
||||
render json: @issue.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }])
|
||||
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def referenced_merge_requests
|
||||
@merge_requests = @issue.referenced_merge_requests(current_user)
|
||||
@closed_by_merge_requests = @issue.closed_by_merge_requests(current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string('projects/issues/_merge_requests')
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def related_branches
|
||||
@related_branches = @issue.related_branches(current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string('projects/issues/_related_branches')
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -123,10 +144,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" })
|
||||
end
|
||||
|
||||
def closed_by_merge_requests
|
||||
@closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def issue
|
||||
@@ -174,7 +191,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
def issue_params
|
||||
params.require(:issue).permit(
|
||||
:title, :assignee_id, :position, :description, :confidential,
|
||||
:milestone_id, :state_event, :task_num, label_ids: []
|
||||
:milestone_id, :due_date, :state_event, :task_num, label_ids: []
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -38,13 +38,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
@merge_requests = @merge_requests.page(params[:page])
|
||||
@merge_requests = @merge_requests.preload(:target_project)
|
||||
|
||||
@label = @project.labels.find_by(title: params[:label_name])
|
||||
@labels = @project.labels.where(title: params[:label_name])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("projects/merge_requests/_merge_requests")
|
||||
html: view_to_html_string("projects/merge_requests/_merge_requests"),
|
||||
labels: @labels.as_json(methods: :text_color)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -148,13 +149,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
|
||||
if @merge_request.valid?
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html do
|
||||
redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
|
||||
@merge_request.target_project, @merge_request])
|
||||
end
|
||||
format.json do
|
||||
render json: @merge_request.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }])
|
||||
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Projects::ProjectMembersController < Projects::ApplicationController
|
||||
# Authorize
|
||||
before_action :authorize_admin_project_member!, except: :leave
|
||||
before_action :authorize_admin_project_member!, except: [:leave, :index]
|
||||
|
||||
def index
|
||||
@project_members = @project.project_members
|
||||
|
||||
@@ -6,7 +6,7 @@ class Projects::ServicesController < Projects::ApplicationController
|
||||
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
|
||||
:colorize_messages, :channels,
|
||||
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
|
||||
:note_events, :build_events,
|
||||
:note_events, :build_events, :wiki_page_events,
|
||||
:notify_only_broken_builds, :add_pusher,
|
||||
:send_from_committer_email, :disable_diffs, :external_wiki_url,
|
||||
:notify, :color,
|
||||
|
||||
@@ -44,7 +44,7 @@ class Projects::WikisController < Projects::ApplicationController
|
||||
|
||||
return render('empty') unless can?(current_user, :create_wiki, @project)
|
||||
|
||||
if @page.update(content, format, message)
|
||||
if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
|
||||
redirect_to(
|
||||
namespace_project_wiki_path(@project.namespace, @project, @page),
|
||||
notice: 'Wiki was successfully updated.'
|
||||
@@ -55,9 +55,9 @@ class Projects::WikisController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@page = WikiPage.new(@project_wiki)
|
||||
@page = WikiPages::CreateService.new(@project, current_user, wiki_params).execute
|
||||
|
||||
if @page.create(wiki_params)
|
||||
if @page.persisted?
|
||||
redirect_to(
|
||||
namespace_project_wiki_path(@project.namespace, @project, @page),
|
||||
notice: 'Wiki was successfully updated.'
|
||||
@@ -122,15 +122,4 @@ class Projects::WikisController < Projects::ApplicationController
|
||||
params[:wiki].slice(:title, :content, :format, :message)
|
||||
end
|
||||
|
||||
def content
|
||||
params[:wiki][:content]
|
||||
end
|
||||
|
||||
def format
|
||||
params[:wiki][:format]
|
||||
end
|
||||
|
||||
def message
|
||||
params[:wiki][:message]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,11 +31,11 @@ class RegistrationsController < Devise::RegistrationsController
|
||||
end
|
||||
|
||||
def after_sign_up_path_for(_resource)
|
||||
new_user_session_path
|
||||
users_almost_there_path
|
||||
end
|
||||
|
||||
def after_inactive_sign_up_path_for(_resource)
|
||||
new_user_session_path
|
||||
users_almost_there_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class UsersController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
before_action :set_user
|
||||
before_action :user
|
||||
before_action :authorize_read_user!, only: [:show]
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
@@ -75,22 +76,26 @@ class UsersController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def set_user
|
||||
@user = User.find_by_username!(params[:username])
|
||||
def authorize_read_user!
|
||||
render_404 unless can?(current_user, :read_user, user)
|
||||
end
|
||||
|
||||
def user
|
||||
@user ||= User.find_by_username!(params[:username])
|
||||
end
|
||||
|
||||
def contributed_projects
|
||||
ContributedProjectsFinder.new(@user).execute(current_user)
|
||||
ContributedProjectsFinder.new(user).execute(current_user)
|
||||
end
|
||||
|
||||
def contributions_calendar
|
||||
@contributions_calendar ||= Gitlab::ContributionsCalendar.
|
||||
new(contributed_projects, @user)
|
||||
new(contributed_projects, user)
|
||||
end
|
||||
|
||||
def load_events
|
||||
# Get user activity feed for projects common for both users
|
||||
@events = @user.recent_events.
|
||||
@events = user.recent_events.
|
||||
merge(projects_for_current_user).
|
||||
references(:project).
|
||||
with_associations.
|
||||
@@ -99,16 +104,16 @@ class UsersController < ApplicationController
|
||||
|
||||
def load_projects
|
||||
@projects =
|
||||
PersonalProjectsFinder.new(@user).execute(current_user)
|
||||
PersonalProjectsFinder.new(user).execute(current_user)
|
||||
.page(params[:page])
|
||||
end
|
||||
|
||||
def load_contributed_projects
|
||||
@contributed_projects = contributed_projects.joined(@user)
|
||||
@contributed_projects = contributed_projects.joined(user)
|
||||
end
|
||||
|
||||
def load_groups
|
||||
@groups = JoinedGroupsFinder.new(@user).execute(current_user)
|
||||
@groups = JoinedGroupsFinder.new(user).execute(current_user)
|
||||
end
|
||||
|
||||
def projects_for_current_user
|
||||
|
||||
@@ -39,6 +39,7 @@ class IssuableFinder
|
||||
items = by_assignee(items)
|
||||
items = by_author(items)
|
||||
items = by_label(items)
|
||||
items = by_due_date(items)
|
||||
sort(items)
|
||||
end
|
||||
|
||||
@@ -117,7 +118,7 @@ class IssuableFinder
|
||||
end
|
||||
|
||||
def filter_by_no_label?
|
||||
labels? && params[:label_name] == Label::None.title
|
||||
labels? && params[:label_name].include?(Label::None.title)
|
||||
end
|
||||
|
||||
def labels
|
||||
@@ -271,7 +272,6 @@ class IssuableFinder
|
||||
items = items.without_label
|
||||
else
|
||||
items = items.with_label(label_names)
|
||||
|
||||
if projects
|
||||
items = items.where(labels: { project_id: projects })
|
||||
end
|
||||
@@ -281,8 +281,44 @@ class IssuableFinder
|
||||
items
|
||||
end
|
||||
|
||||
def by_due_date(items)
|
||||
if due_date?
|
||||
if filter_by_no_due_date?
|
||||
items = items.without_due_date
|
||||
elsif filter_by_overdue?
|
||||
items = items.due_before(Date.today)
|
||||
elsif filter_by_due_this_week?
|
||||
items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week)
|
||||
elsif filter_by_due_this_month?
|
||||
items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month)
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
def filter_by_no_due_date?
|
||||
due_date? && params[:due_date] == Issue::NoDueDate.name
|
||||
end
|
||||
|
||||
def filter_by_overdue?
|
||||
due_date? && params[:due_date] == Issue::Overdue.name
|
||||
end
|
||||
|
||||
def filter_by_due_this_week?
|
||||
due_date? && params[:due_date] == Issue::DueThisWeek.name
|
||||
end
|
||||
|
||||
def filter_by_due_this_month?
|
||||
due_date? && params[:due_date] == Issue::DueThisMonth.name
|
||||
end
|
||||
|
||||
def due_date?
|
||||
params[:due_date].present? && klass.column_names.include?('due_date')
|
||||
end
|
||||
|
||||
def label_names
|
||||
params[:label_name].split(',')
|
||||
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
|
||||
end
|
||||
|
||||
def current_user_related?
|
||||
|
||||
@@ -254,11 +254,11 @@ module ApplicationHelper
|
||||
|
||||
def page_filter_path(options = {})
|
||||
without = options.delete(:without)
|
||||
add_label = options.delete(:label)
|
||||
|
||||
exist_opts = {
|
||||
state: params[:state],
|
||||
scope: params[:scope],
|
||||
label_name: params[:label_name],
|
||||
milestone_title: params[:milestone_title],
|
||||
assignee_id: params[:assignee_id],
|
||||
author_id: params[:author_id],
|
||||
@@ -275,6 +275,13 @@ module ApplicationHelper
|
||||
|
||||
path = request.path
|
||||
path << "?#{options.to_param}"
|
||||
if add_label
|
||||
if params[:label_name].present? and params[:label_name].respond_to?('any?')
|
||||
params[:label_name].each do |label|
|
||||
path << "&label_name[]=#{label}"
|
||||
end
|
||||
end
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user