diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-11-18 20:20:30 +0100 |
---|---|---|
committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-11-18 20:20:30 +0100 |
commit | 8d0018444db47b38d1efbd5589d0b147740d4bc3 (patch) | |
tree | 09eaa4fac817c623a3019b951fffc767d53d13ec /app | |
parent | 73a5f331b2e7b2e224147e69c857c6306318399e (diff) | |
parent | 596c305eab94dfacb302c3630f08d81636623d64 (diff) | |
download | gitlab-ce-8d0018444db47b38d1efbd5589d0b147740d4bc3.tar.gz |
Merge remote-tracking branch 'origin/master' into 22539-display-folders
Diffstat (limited to 'app')
246 files changed, 1867 insertions, 759 deletions
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 919107b8cb9..906a1a69d93 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-undef, quotes, no-var, padded-blocks, max-len */ (function() { this.Activities = (function() { function Activities() { diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 1ef340e4ca1..31852e4750c 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, no-undef, padded-blocks, max-len */ (function() { this.Admin = (function() { function Admin() { diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 1cab66e109e..1c625e2f2b1 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, no-undef, comma-dangle, prefer-arrow-callback, indent, object-curly-spacing, quote-props, no-param-reassign, padded-blocks, max-len */ (function() { this.Api = { groupsPath: "/api/:version/groups.json", diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8d8431c424e..76f3c6506ed 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, no-undef, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len */ // This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js index c7eff27f971..9417afc2ea7 100644 --- a/app/assets/javascripts/aside.js +++ b/app/assets/javascripts/aside.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, padded-blocks, max-len */ (function() { this.Aside = (function() { function Aside() { diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js index ab09e4475e6..f45dbe4cbf2 100644 --- a/app/assets/javascripts/autosave.js +++ b/app/assets/javascripts/autosave.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, quotes, prefer-template, no-var, one-var, no-unused-vars, one-var-declaration-per-line, no-void, consistent-return, no-empty, padded-blocks, max-len */ (function() { this.Autosave = (function() { function Autosave(field, key) { diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index d7cda977845..f4302e2e9f6 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, no-var, spaced-comment, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-template, quotes, comma-dangle, no-param-reassign, no-void, radix, keyword-spacing, space-before-blocks, brace-style, no-underscore-dangle, no-undef, no-plusplus, no-return-assign, camelcase, padded-blocks, max-len */ (function() { this.AwardsHandler = (function() { var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js index 074378b9e52..a5d62f881fe 100644 --- a/app/assets/javascripts/behaviors/autosize.js +++ b/app/assets/javascripts/behaviors/autosize.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, no-undef, padded-blocks, max-len */ /*= require jquery.ba-resize */ /*= require autosize */ diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js index a64cefb62bd..3998ee9a0a0 100644 --- a/app/assets/javascripts/behaviors/details_behavior.js +++ b/app/assets/javascripts/behaviors/details_behavior.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, padded-blocks, max-len */ (function() { $(function() { $("body").on("click", ".js-details-target", function() { diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js index 7ff88ecdcaf..4edcaa15fe5 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js +++ b/app/assets/javascripts/behaviors/quick_submit.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-undef, prefer-arrow-callback, camelcase, max-len, consistent-return, quotes, object-shorthand, comma-dangle, padded-blocks, max-len */ // Quick Submit behavior // // When a child field of a form with a `js-quick-submit` class receives a diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js index 4ac343f876c..72362988b2e 100644 --- a/app/assets/javascripts/behaviors/requires_input.js +++ b/app/assets/javascripts/behaviors/requires_input.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, no-else-return, consistent-return, padded-blocks, max-len */ // Requires Input behavior // // When called on a form with input fields with the `required` attribute, the diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js index 05b213fe3fb..6a49715590c 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.js +++ b/app/assets/javascripts/behaviors/toggler_behavior.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */ (function(w) { $(function() { // Toggle button. Show/hide content inside parent container. diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js index 33fb4f8185c..e0a2e8ac12e 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js +++ b/app/assets/javascripts/blob/blob_file_dropzone.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, camelcase, no-undef, object-shorthand, quotes, comma-dangle, prefer-arrow-callback, no-unused-vars, prefer-template, no-useless-escape, no-alert, padded-blocks, max-len */ (function() { this.BlobFileDropzone = (function() { function BlobFileDropzone(form, method) { diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js b/app/assets/javascripts/blob/blob_gitignore_selector.js index 344fe5dcd94..7e8f1062ab3 100644 --- a/app/assets/javascripts/blob/blob_gitignore_selector.js +++ b/app/assets/javascripts/blob/blob_gitignore_selector.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, no-undef, padded-blocks, max-len */ /*= require blob/template_selector */ diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js index 9e992f7913c..9a694daa010 100644 --- a/app/assets/javascripts/blob/blob_gitignore_selectors.js +++ b/app/assets/javascripts/blob/blob_gitignore_selectors.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-cond-assign, no-sequences, no-undef, comma-dangle, padded-blocks, max-len */ (function() { this.BlobGitignoreSelectors = (function() { function BlobGitignoreSelectors(opts) { diff --git a/app/assets/javascripts/blob/blob_license_selector.js b/app/assets/javascripts/blob/blob_license_selector.js index 41a83a56146..9a77fe35d55 100644 --- a/app/assets/javascripts/blob/blob_license_selector.js +++ b/app/assets/javascripts/blob/blob_license_selector.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, comma-dangle, no-undef, padded-blocks, max-len */ /*= require blob/template_selector */ diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js index b801c10f168..b8eb0f60a8e 100644 --- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_edit_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-undef, no-new, padded-blocks, max-len */ /*= require_tree . */ (function() { diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index 60840560dd3..0c74aaaa852 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, no-param-reassign, no-undef, quotes, prefer-template, no-new, comma-dangle, one-var, one-var-declaration-per-line, prefer-arrow-callback, no-else-return, no-unused-vars, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/boards/test_utils/simulate_drag.js b/app/assets/javascripts/boards/test_utils/simulate_drag.js index 039ca491cf5..01e09ec482e 100644 --- a/app/assets/javascripts/boards/test_utils/simulate_drag.js +++ b/app/assets/javascripts/boards/test_utils/simulate_drag.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable wrap-iife, func-names, strict, indent, no-tabs, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, object-curly-spacing, no-unused-expressions, prefer-arrow-callback, max-len */ (function () { 'use strict'; diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js index 5d4d23e26c6..e7ceb602601 100644 --- a/app/assets/javascripts/breakpoints.js +++ b/app/assets/javascripts/breakpoints.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, padded-blocks, no-return-assign, new-parens, no-param-reassign, no-undef, max-len */ (function() { this.Breakpoints = (function() { var BreakpointInstance, instance; diff --git a/app/assets/javascripts/broadcast_message.js b/app/assets/javascripts/broadcast_message.js index 576f4c76c1e..30432dae278 100644 --- a/app/assets/javascripts/broadcast_message.js +++ b/app/assets/javascripts/broadcast_message.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, padded-blocks, max-len */ (function() { $(function() { var previewPath; diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 5133e361001..68012e8cf42 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-param-reassign, no-undef, quotes, yoda, no-else-return, consistent-return, comma-dangle, semi, object-shorthand, prefer-template, one-var, one-var-declaration-per-line, no-unused-vars, max-len, vars-on-top, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js index 49f84581650..c423a548a30 100644 --- a/app/assets/javascripts/build_artifacts.js +++ b/app/assets/javascripts/build_artifacts.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, padded-blocks, max-len */ (function() { this.BuildArtifacts = (function() { function BuildArtifacts() { diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js index fac5b4f17da..67509ea7d91 100644 --- a/app/assets/javascripts/commit.js +++ b/app/assets/javascripts/commit.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-undef, padded-blocks */ (function() { this.Commit = (function() { function Commit() { diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js index 16d63729d31..3f29826fa9b 100644 --- a/app/assets/javascripts/commit/file.js +++ b/app/assets/javascripts/commit/file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, padded-blocks, max-len */ (function() { this.CommitFile = (function() { function CommitFile(file) { diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index ffddce1297b..4c2ae595319 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, padded-blocks, max-len */ (function() { this.ImageFile = (function() { var prepareFrames; diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index c765d233831..951fb338f9d 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len */ (function() { this.CommitsList = (function() { function CommitsList() {} diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index 61cc91c524b..d4243baadb5 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, padded-blocks, max-len */ (function() { this.Compare = (function() { function Compare(opts) { diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index 143d21adb37..686a48486f3 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, padded-blocks, max-len */ (function() { this.ConfirmDangerModal = (function() { function ConfirmDangerModal(form, text) { diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js index 7808d7fe313..1cc34e490c2 100644 --- a/app/assets/javascripts/copy_to_clipboard.js +++ b/app/assets/javascripts/copy_to_clipboard.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-undef, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, padded-blocks, max-len */ /*= require clipboard */ diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 82bfdcea0ca..00da5f17f9f 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, max-len, one-var, camelcase, one-var-declaration-per-line, no-unused-vars, no-unused-expressions, no-sequences, object-shorthand, comma-dangle, prefer-arrow-callback, semi, radix, padded-blocks, max-len */ (function() { this.Diff = (function() { var UNFOLD_COUNT; diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 1a0aa9757ba..e1e76bca6ad 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, no-undef, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, no-plusplus, prefer-arrow-callback, padded-blocks, max-len */ /*= require preview_markdown */ diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js index 4c9e219aa43..fc6c130113d 100644 --- a/app/assets/javascripts/extensions/array.js +++ b/app/assets/javascripts/extensions/array.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */ Array.prototype.first = function() { return this[0]; } diff --git a/app/assets/javascripts/extensions/jquery.js b/app/assets/javascripts/extensions/jquery.js index 623a80b7053..cdedc865d1b 100644 --- a/app/assets/javascripts/extensions/jquery.js +++ b/app/assets/javascripts/extensions/jquery.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, comma-dangle, padded-blocks, max-len */ // Disable an element and add the 'disabled' Bootstrap class (function() { $.fn.extend({ diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 732136f1f2c..0122e847161 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, padded-blocks, consistent-return, no-undef, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js index 46e272c3311..804d7d9c4ab 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/flash.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-param-reassign, quotes, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */ (function() { this.Flash = (function() { var hideFlash; diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 58c1179d250..5d9ac4d350a 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -35,7 +35,7 @@ DefaultOptions: { sorter: function(query, items, searchKey) { // Highlight first item only if at least one char was typed - this.setting.highlightFirst = query.length > 0; + this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0; if ((items[0].name != null) && items[0].name === 'loading') { return items; } @@ -112,6 +112,7 @@ insertTpl: '${atwho-at}${username}', searchKey: 'search', data: ['loading'], + alwaysHighlightFirst: true, callbacks: { sorter: this.DefaultOptions.sorter, filter: this.DefaultOptions.filter, diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 98e43c4d088..969778dded7 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, space-before-blocks, prefer-rest-params, max-len, vars-on-top, no-plusplus, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, semi, no-return-assign, no-else-return, camelcase, no-undef, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, padded-blocks, prefer-template, no-param-reassign, no-loop-func, no-extra-semi, keyword-spacing, no-mixed-operators, max-len */ (function() { var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, @@ -249,7 +249,7 @@ _this.fullData = data; _this.parseData(_this.fullData); _this.focusTextInput(); - if (_this.options.filterable && _this.filter && _this.filter.input) { + if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') { return _this.filter.input.trigger('input'); } }; diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index ce54c34492d..db5d9e75b3a 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-undef, no-new, padded-blocks, max-len */ (function() { this.GLForm = (function() { function GLForm(form) { diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js index e103748d499..32c26349da0 100644 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ b/app/assets/javascripts/graphs/graphs_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ // This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js diff --git a/app/assets/javascripts/graphs/stat_graph.js b/app/assets/javascripts/graphs/stat_graph.js index b796a9abb49..3273bf3a263 100644 --- a/app/assets/javascripts/graphs/stat_graph.js +++ b/app/assets/javascripts/graphs/stat_graph.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-return-assign, padded-blocks, max-len */ (function() { this.StatGraph = (function() { function StatGraph() {} diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js index 818bff0c413..c3a132b3c75 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, no-undef, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, padded-blocks, max-len */ /*= require d3 */ diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js index dea26a3f1e1..cb2448e8cc7 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, space-before-blocks, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, padded-blocks, no-undef, newline-per-chained-call, no-else-return, max-len */ /*= require d3 */ diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js index 362a77e868f..051ff98c774 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, no-plusplus, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, padded-blocks, max-len */ (function() { window.ContributorsStatGraphUtil = { parse_log: function(log) { diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js index 774477dc7a9..17a76168a79 100644 --- a/app/assets/javascripts/group_avatar.js +++ b/app/assets/javascripts/group_avatar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, one-var, one-var-declaration-per-line, no-useless-escape, padded-blocks, max-len */ (function() { this.GroupAvatar = (function() { function GroupAvatar() { diff --git a/app/assets/javascripts/group_label_subscription.js.es6 b/app/assets/javascripts/group_label_subscription.js.es6 new file mode 100644 index 00000000000..eea6cd40859 --- /dev/null +++ b/app/assets/javascripts/group_label_subscription.js.es6 @@ -0,0 +1,53 @@ +/* eslint-disable */ +(function(global) { + class GroupLabelSubscription { + constructor(container) { + const $container = $(container); + this.$dropdown = $container.find('.dropdown'); + this.$subscribeButtons = $container.find('.js-subscribe-button'); + this.$unsubscribeButtons = $container.find('.js-unsubscribe-button'); + + this.$subscribeButtons.on('click', this.subscribe.bind(this)); + this.$unsubscribeButtons.on('click', this.unsubscribe.bind(this)); + } + + unsubscribe(event) { + event.preventDefault(); + + const url = this.$unsubscribeButtons.attr('data-url'); + + $.ajax({ + type: 'POST', + url: url + }).done(() => { + this.toggleSubscriptionButtons(); + this.$unsubscribeButtons.removeAttr('data-url'); + }); + } + + subscribe(event) { + event.preventDefault(); + + const $btn = $(event.currentTarget); + const url = $btn.attr('data-url'); + + this.$unsubscribeButtons.attr('data-url', url); + + $.ajax({ + type: 'POST', + url: url + }).done(() => { + this.toggleSubscriptionButtons(); + }); + } + + toggleSubscriptionButtons() { + this.$dropdown.toggleClass('hidden'); + this.$subscribeButtons.toggleClass('hidden'); + this.$unsubscribeButtons.toggleClass('hidden'); + } + } + + global.GroupLabelSubscription = GroupLabelSubscription; + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index e3c39c895ba..3dc6f05ca20 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var, camelcase, one-var-declaration-per-line, quotes, object-shorthand, no-undef, prefer-arrow-callback, comma-dangle, consistent-return, yoda, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, padded-blocks, max-len */ (function() { var slice = [].slice; diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js index 81fcaf06430..c7cbf9ca44b 100644 --- a/app/assets/javascripts/header.js +++ b/app/assets/javascripts/header.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable wrap-iife, func-names, space-before-function-paren, padded-blocks, prefer-arrow-callback, no-var, max-len */ (function() { $(document).on('todo:toggle', function(e, count) { diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index c53f7c88aa2..9425b6ed9d4 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, camelcase, no-var, one-var, one-var-declaration-per-line, prefer-template, quotes, object-shorthand, comma-dangle, no-unused-vars, prefer-arrow-callback, no-else-return, padded-blocks, vars-on-top, no-new, no-undef, max-len */ (function() { this.ImporterStatus = (function() { function ImporterStatus(jobs_url, import_url) { diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index fae49ee6144..317818951fd 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, padded-blocks, max-len */ (function() { this.IssuableContext = (function() { function IssuableContext(currentUser) { diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 849b45756ee..50fdbc89c7c 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-undef, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, radix, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 67ace697936..8540b199aba 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-undef, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, padded-blocks, max-len */ /*= require flash */ /*= require jquery.waitforimages */ diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js index d7262e5eb74..b39d8274e13 100644 --- a/app/assets/javascripts/issue_status_select.js +++ b/app/assets/javascripts/issue_status_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */ (function() { this.IssueStatusSelect = (function() { function IssueStatusSelect() { diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js index 3033e8ca5c2..10de13c9a8a 100644 --- a/app/assets/javascripts/labels.js +++ b/app/assets/javascripts/labels.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index c334e3e0c02..812d5cde685 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */ (function() { this.LabelsSelect = (function() { function LabelsSelect() { diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 6b4edf02f4d..2b700539c2b 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, indent, vars-on-top, padded-blocks, max-len */ (function() { var hideEndFade; diff --git a/app/assets/javascripts/lib/chart.js b/app/assets/javascripts/lib/chart.js index e1dfdae97de..d8ad5aaeffe 100644 --- a/app/assets/javascripts/lib/chart.js +++ b/app/assets/javascripts/lib/chart.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require Chart */ diff --git a/app/assets/javascripts/lib/cropper.js b/app/assets/javascripts/lib/cropper.js index 155e30cc462..5221f85ba7a 100644 --- a/app/assets/javascripts/lib/cropper.js +++ b/app/assets/javascripts/lib/cropper.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require cropper */ diff --git a/app/assets/javascripts/lib/d3.js b/app/assets/javascripts/lib/d3.js index 0c9c2787077..57e7986576c 100644 --- a/app/assets/javascripts/lib/d3.js +++ b/app/assets/javascripts/lib/d3.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require d3 */ diff --git a/app/assets/javascripts/lib/raphael.js b/app/assets/javascripts/lib/raphael.js index cc445db274b..5a9a501efe3 100644 --- a/app/assets/javascripts/lib/raphael.js +++ b/app/assets/javascripts/lib/raphael.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require raphael */ /*= require g.raphael */ diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js index a68edab2aad..83957af94f3 100644 --- a/app/assets/javascripts/lib/utils/animate.js +++ b/app/assets/javascripts/lib/utils/animate.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, padded-blocks, max-len */ (function() { (function(w) { if (w.gl == null) { diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6cb3d95f984..d83c41fae9d 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-param-reassign, no-else-return, quotes, object-shorthand, comma-dangle, camelcase, one-var, vars-on-top, one-var-declaration-per-line, no-return-assign, consistent-return, padded-blocks, max-len */ (function() { (function(w) { var base; @@ -125,6 +125,11 @@ // Close any open tooltips $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy'); }; + + gl.utils.isMetaKey = function(e) { + return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; + }; + })(window); }).call(this); diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 3965109dd65..d480fdc882b 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-undef, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js index dafc006d2e5..d0fe69260a5 100644 --- a/app/assets/javascripts/lib/utils/notify.js +++ b/app/assets/javascripts/lib/utils/notify.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, no-undef, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, max-len */ (function() { (function(w) { var notificationGranted, notifyMe, notifyPermissions; diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index efa25958b15..ac44b81ee22 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, semi, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/timeago.js b/app/assets/javascripts/lib/utils/timeago.js index 42606dd2d46..edf0a612374 100644 --- a/app/assets/javascripts/lib/utils/timeago.js +++ b/app/assets/javascripts/lib/utils/timeago.js @@ -1,3 +1,5 @@ +/* eslint-disable no-unused-expressions, wrap-iife, func-names, curly, no-param-reassign, no-trailing-spaces, prefer-arrow-callback, no-var, one-var, quote-props, space-before-function-paren, vars-on-top, radix, prefer-template, space-infix-ops, no-use-before-define, newline-per-chained-call, no-useless-escape, no-nested-ternary, indent, no-undef, no-plusplus, one-var-declaration-per-line, operator-assignment, consistent-return, keyword-spacing, max-len, space-unary-ops, no-shadow, no-restricted-syntax, guard-for-in, eol-last, max-len */ + /** * Copyright (c) 2016 hustcc * License: MIT diff --git a/app/assets/javascripts/lib/utils/type_utility.js b/app/assets/javascripts/lib/utils/type_utility.js index 4fd1e3fc1d3..961859dfb5b 100644 --- a/app/assets/javascripts/lib/utils/type_utility.js +++ b/app/assets/javascripts/lib/utils/type_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-return-assign, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 44a66a915e3..6872186cd7f 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, no-plusplus, guard-for-in, no-restricted-syntax, prefer-template, quotes, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index ea5a60bb78e..b0f834705c3 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-param-reassign, no-undef, prefer-template, quotes, comma-dangle, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, spaced-comment, radix, no-else-return, max-len, no-plusplus, padded-blocks, max-len */ // LineHighlighter // // Handles single- and multi-line selection and highlight for blob views. diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js index d4f86534f0c..9404b2c3a8c 100644 --- a/app/assets/javascripts/logo.js +++ b/app/assets/javascripts/logo.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, padded-blocks */ (function() { Turbolinks.enableProgressBar(); diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js index 0bd90c57396..7741cd29793 100644 --- a/app/assets/javascripts/member_expiration_date.js +++ b/app/assets/javascripts/member_expiration_date.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, vars-on-top, no-var, object-shorthand, comma-dangle, max-len */ (function() { // Add datepickers to all `js-access-expiration-date` elements. If those elements are // children of an element with the `clearable-input` class, and have a sibling diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index d3bd1e846c1..a4b4db14db8 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-undef, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, padded-blocks, max-len */ /*= require jquery.waitforimages */ /*= require task_list */ diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 5ca4b34909a..b1928f8d279 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable max-len, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-undef, one-var, one-var-declaration-per-line, quotes, comma-dangle, consistent-return, prefer-template, no-param-reassign, camelcase, vars-on-top, space-in-parens, curly, prefer-arrow-callback, no-unused-vars, no-return-assign, semi, object-shorthand, operator-assignment, padded-blocks, max-len */ // MergeRequestTabs // // Handles persisting and restoring the current tab selection and lazily-loading @@ -145,7 +145,8 @@ if (action === 'show') { action = 'notes'; } - $(".merge-request-tabs a[data-action='" + action + "']").tab('show').trigger('shown.bs.tab'); + // important note: the .tab('show') method triggers 'shown.bs.tab' event itself + $(".merge-request-tabs a[data-action='" + action + "']").tab('show'); }; // Replaces the current Merge Request-specific action in the URL with a new one diff --git a/app/assets/javascripts/merged_buttons.js b/app/assets/javascripts/merged_buttons.js index 7ad86d8c084..15a12c3d985 100644 --- a/app/assets/javascripts/merged_buttons.js +++ b/app/assets/javascripts/merged_buttons.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 9299c96e8ea..db7561a3a75 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, no-undef, comma-dangle, no-var, prefer-template, no-underscore-dangle, consistent-return, one-var, one-var-declaration-per-line, default-case, prefer-arrow-callback, padded-blocks, max-len */ (function() { this.Milestone = (function() { Milestone.updateIssue = function(li, issue_url, data) { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index d1cd38ad110..67796083790 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-undef, no-param-reassign, no-shadow, padded-blocks, max-len */ (function() { this.MilestoneSelect = (function() { function MilestoneSelect(currentProject) { diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js index d1168227b77..87c903ec576 100644 --- a/app/assets/javascripts/namespace_select.js +++ b/app/assets/javascripts/namespace_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, vars-on-top, one-var-declaration-per-line, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, no-undef, prefer-arrow-callback, padded-blocks, no-param-reassign, no-cond-assign, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js index 74dbeb94741..e3dc599b90a 100644 --- a/app/assets/javascripts/network/branch_graph.js +++ b/app/assets/javascripts/network/branch_graph.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-mixed-operators, new-cap, no-undef, no-plusplus, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/network/network.js b/app/assets/javascripts/network/network.js index 8898e7ace43..5a8f723a27b 100644 --- a/app/assets/javascripts/network/network.js +++ b/app/assets/javascripts/network/network.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, no-undef, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */ (function() { this.Network = (function() { function Network(opts) { diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js index a192273a180..732d92845cb 100644 --- a/app/assets/javascripts/network/network_bundle.js +++ b/app/assets/javascripts/network/network_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, camelcase, no-undef, comma-dangle, consistent-return, padded-blocks, max-len */ // This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js index 0e643b0ff14..29a323dd4c6 100644 --- a/app/assets/javascripts/new_branch_form.js +++ b/app/assets/javascripts/new_branch_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, space-before-blocks, prefer-rest-params, max-len, vars-on-top, no-plusplus, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js index acb529023fa..8fb8f3e4a5f 100644 --- a/app/assets/javascripts/new_commit_form.js +++ b/app/assets/javascripts/new_commit_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-return-assign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index df7e316ca6c..6cb87f9ba81 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */ /*= require autosave */ /*= require autosize */ @@ -12,7 +12,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; this.Notes = (function() { - var isMetaKey; + const MAX_VISIBLE_COMMIT_LIST_COUNT = 3; Notes.interval = null; @@ -33,6 +33,7 @@ this.resetMainTargetForm = bind(this.resetMainTargetForm, this); this.refresh = bind(this.refresh, this); this.keydownNoteText = bind(this.keydownNoteText, this); + this.toggleCommitList = bind(this.toggleCommitList, this); this.notes_url = notes_url; this.note_ids = note_ids; this.last_fetched_at = last_fetched_at; @@ -46,6 +47,7 @@ this.setPollingInterval(); this.setupMainTargetNoteForm(); this.initTaskList(); + this.collapseLongCommitList(); } Notes.prototype.addBinding = function() { @@ -81,10 +83,13 @@ $(document).on("click", ".js-add-diff-note-button", this.addDiffNote); // hide diff note form $(document).on("click", ".js-close-discussion-note-form", this.cancelDiscussionForm); + // toggle commit list + $(document).on("click", '.system-note-commit-list-toggler', this.toggleCommitList); // fetch notes when tab becomes visible $(document).on("visibilitychange", this.visibilityChange); // when issue status changes, we need to refresh data $(document).on("issuable:change", this.refresh); + // when a key is clicked on the notes return $(document).on("keydown", ".js-note-text", this.keydownNoteText); }; @@ -114,9 +119,10 @@ Notes.prototype.keydownNoteText = function(e) { var $textarea, discussionNoteForm, editNote, myLastNote, myLastNoteEditBtn, newText, originalText; - if (isMetaKey(e)) { + if (gl.utils.isMetaKey(e)) { return; } + $textarea = $(e.target); // Edit previous note when UP arrow is hit switch (e.which) { @@ -156,10 +162,6 @@ } }; - isMetaKey = function(e) { - return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; - }; - Notes.prototype.initRefresh = function() { clearInterval(Notes.interval); return Notes.interval = setInterval((function(_this) { @@ -263,6 +265,7 @@ $notesList.append(note.html).syntaxHighlight(); // Update datetime format on the recent note gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false); + this.collapseLongCommitList(); this.initTaskList(); this.refresh(); return this.updateNotesCount(1); @@ -433,9 +436,9 @@ var $form = $(xhr.target); if ($form.attr('data-resolve-all') != null) { - var projectPath = $form.data('project-path') - discussionId = $form.data('discussion-id'), - mergeRequestId = $form.data('noteable-iid'); + var projectPath = $form.data('project-path'); + var discussionId = $form.data('discussion-id'); + var mergeRequestId = $form.data('noteable-iid'); if (ResolveService != null) { ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId); @@ -844,9 +847,9 @@ return this.notesCountBadge.text(parseInt(this.notesCountBadge.text()) + updateCount); }; - Notes.prototype.resolveDiscussion = function () { - var $this = $(this), - discussionId = $this.attr('data-discussion-id'); + Notes.prototype.resolveDiscussion = function() { + var $this = $(this); + var discussionId = $this.attr('data-discussion-id'); $this .closest('form') @@ -855,6 +858,36 @@ .attr('data-project-path', $this.attr('data-project-path')); }; + Notes.prototype.toggleCommitList = function(e) { + const $element = $(e.target); + const $closestSystemCommitList = $element.siblings('.system-note-commit-list'); + + $closestSystemCommitList.toggleClass('hide-shade'); + }; + + /** + Scans system notes with `ul` elements in system note body + then collapse long commit list pushed by user to make it less + intrusive. + */ + Notes.prototype.collapseLongCommitList = function() { + const systemNotes = $('#notes-list').find('li.system-note').has('ul'); + + $.each(systemNotes, function(index, systemNote) { + const $systemNote = $(systemNote); + const headerMessage = $systemNote.find('.note-text').find('p:first').text().replace(':', ''); + + $systemNote.find('.note-header .system-note-message').html(headerMessage); + + if ($systemNote.find('li').length > MAX_VISIBLE_COMMIT_LIST_COUNT) { + $systemNote.find('.note-text').addClass('system-note-commit-list'); + $systemNote.find('.system-note-commit-list-toggler').show(); + } else { + $systemNote.find('.note-text').addClass('system-note-commit-list hide-shade'); + } + }); + }; + return Notes; })(); diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index ef3f2c6ae73..b152d26733f 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, no-undef, padded-blocks, max-len */ (function() { this.NotificationsDropdown = (function() { function NotificationsDropdown() { diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js index 6fbec8efe9b..2034f9a748a 100644 --- a/app/assets/javascripts/notifications_form.js +++ b/app/assets/javascripts/notifications_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index 2e4dc62273e..d22d2d9dbae 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-undef, prefer-template, wrap-iife, comma-dangle, no-return-assign, no-else-return, consistent-return, no-unused-vars, padded-blocks, max-len */ (function() { this.Pager = { init: function(limit, preload, disable, callback) { diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index e6fada5c84c..a84db9c0233 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -3,26 +3,12 @@ class Pipelines { constructor() { - this.initGraphToggle(); this.addMarginToBuildColumns(); } - initGraphToggle() { - this.pipelineGraph = document.querySelector('.pipeline-graph'); - this.toggleButton = document.querySelector('.toggle-pipeline-btn'); - this.toggleButtonText = this.toggleButton.querySelector('.toggle-btn-text'); - this.toggleButton.addEventListener('click', this.toggleGraph.bind(this)); - } - - toggleGraph() { - const graphCollapsed = this.pipelineGraph.classList.contains('graph-collapsed'); - this.toggleButton.classList.toggle('graph-collapsed'); - this.pipelineGraph.classList.toggle('graph-collapsed'); - this.toggleButtonText.textContent = graphCollapsed ? 'Hide' : 'Expand'; - } - addMarginToBuildColumns() { - const secondChildBuildNodes = this.pipelineGraph.querySelectorAll('.build:nth-child(2)'); + this.pipelineGraph = document.querySelector('.pipeline-graph'); + const secondChildBuildNodes = document.querySelector('.pipeline-graph').querySelectorAll('.build:nth-child(2)'); for (buildNodeIndex in secondChildBuildNodes) { const buildNode = secondChildBuildNodes[buildNodeIndex]; const firstChildBuildNode = buildNode.previousElementSibling; diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index f2a45a18bed..3723aa24942 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, no-undef, camelcase, prefer-arrow-callback, max-len */ // MarkdownPreview // // Handles toggling the "Write" and "Preview" tab clicks, rendering the preview, diff --git a/app/assets/javascripts/profile/profile_bundle.js b/app/assets/javascripts/profile/profile_bundle.js index 22bee0f6187..f50802bdf2e 100644 --- a/app/assets/javascripts/profile/profile_bundle.js +++ b/app/assets/javascripts/profile/profile_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require_tree . */ diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 2d0c6b16699..016d999d77e 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, no-undef, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-else-return, newline-per-chained-call, no-shadow, semi, vars-on-top, indent, prefer-template, padded-blocks, max-len */ (function() { this.Project = (function() { function Project() { diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js index 61877c6616d..84f28ede4bf 100644 --- a/app/assets/javascripts/project_avatar.js +++ b/app/assets/javascripts/project_avatar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-useless-escape, padded-blocks, max-len */ (function() { this.ProjectAvatar = (function() { function ProjectAvatar() { diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index ddac5ed83e1..804306a3293 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, no-undef, object-shorthand, no-param-reassign, comma-dangle, no-plusplus, prefer-template, no-unused-vars, no-return-assign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js index fd95f8f2c19..4aedc9a2330 100644 --- a/app/assets/javascripts/project_fork.js +++ b/app/assets/javascripts/project_fork.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, padded-blocks, max-len */ (function() { this.ProjectFork = (function() { function ProjectFork() { diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js index f1c4a9fe542..c99e55234cf 100644 --- a/app/assets/javascripts/project_import.js +++ b/app/assets/javascripts/project_import.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-undef, padded-blocks, max-len */ (function() { this.ProjectImport = (function() { function ProjectImport() { diff --git a/app/assets/javascripts/project_label_subscription.js.es6 b/app/assets/javascripts/project_label_subscription.js.es6 new file mode 100644 index 00000000000..03a115cb35b --- /dev/null +++ b/app/assets/javascripts/project_label_subscription.js.es6 @@ -0,0 +1,53 @@ +/* eslint-disable */ +(function(global) { + class ProjectLabelSubscription { + constructor(container) { + this.$container = $(container); + this.$buttons = this.$container.find('.js-subscribe-button'); + + this.$buttons.on('click', this.toggleSubscription.bind(this)); + } + + toggleSubscription(event) { + event.preventDefault(); + + const $btn = $(event.currentTarget); + const $span = $btn.find('span'); + const url = $btn.attr('data-url'); + const oldStatus = $btn.attr('data-status'); + + $btn.addClass('disabled'); + $span.toggleClass('hidden'); + + $.ajax({ + type: 'POST', + url: url + }).done(() => { + let newStatus, newAction; + + if (oldStatus === 'unsubscribed') { + [newStatus, newAction] = ['subscribed', 'Unsubscribe']; + } else { + [newStatus, newAction] = ['unsubscribed', 'Subscribe']; + } + + $span.toggleClass('hidden'); + $btn.removeClass('disabled'); + + this.$buttons.attr('data-status', newStatus); + this.$buttons.find('> span').text(newAction); + + for (let button of this.$buttons) { + let $button = $(button); + + if ($button.attr('data-original-title')) { + $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle'); + } + } + }); + } + } + + global.ProjectLabelSubscription = ProjectLabelSubscription; + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index 0d3fb31a9cf..7fc611d0dad 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-vars, one-var, indent, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, radix, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index e1acf3c8232..fe1f96872f3 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-undef, no-else-return, quotes, padded-blocks, max-len */ (function() { this.ProjectSelect = (function() { function ProjectSelect() { diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js index 21650f5f67a..eaf4c03d573 100644 --- a/app/assets/javascripts/project_show.js +++ b/app/assets/javascripts/project_show.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */ (function() { this.ProjectShow = (function() { function ProjectShow() {} diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js index 3458cd89ae2..dbf530bed41 100644 --- a/app/assets/javascripts/projects_list.js +++ b/app/assets/javascripts/projects_list.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, no-undef, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, padded-blocks, max-len */ (function() { this.ProjectsList = { init: function() { diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index df38937858f..440b5da756d 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-vars, semi, consistent-return, one-var, one-var-declaration-per-line, no-undef, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index d79e6f014f6..1d208f1494c 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, no-undef, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, padded-blocks, max-len */ (function() { this.Search = (function() { function Search() { diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index 8d8ab6dda5e..fa2168723be 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-undef, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-plusplus, no-else-return, comma-dangle, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js index 704a8bd3a57..65305b8c22f 100644 --- a/app/assets/javascripts/shortcuts_blob.js +++ b/app/assets/javascripts/shortcuts_blob.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, consistent-return, padded-blocks, no-undef, max-len */ /*= require shortcuts */ diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js index befe4eccdba..1b9a265ba39 100644 --- a/app/assets/javascripts/shortcuts_dashboard_navigation.js +++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign, padded-blocks, no-undef, max-len */ /*= require shortcuts */ diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js index 90ed4267661..68cd6fad04e 100644 --- a/app/assets/javascripts/shortcuts_find_file.js +++ b/app/assets/javascripts/shortcuts_find_file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, padded-blocks, no-undef, max-len */ /*= require shortcuts_navigation */ diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 25ec7dbc067..c4899f3566a 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, one-var-declaration-per-line, quotes, prefer-arrow-callback, consistent-return, prefer-template, no-mixed-operators, no-undef, padded-blocks, max-len */ /*= require mousetrap */ /*= require shortcuts_navigation */ diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js index 19c6b7d30ab..7d4d6364c70 100644 --- a/app/assets/javascripts/shortcuts_navigation.js +++ b/app/assets/javascripts/shortcuts_navigation.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign, padded-blocks, no-undef, max-len */ /*= require shortcuts */ diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js index 002e979a2c6..a4095d2c06b 100644 --- a/app/assets/javascripts/shortcuts_network.js +++ b/app/assets/javascripts/shortcuts_network.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, padded-blocks, no-undef, max-len */ /*= require shortcuts_navigation */ diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 01ccbe5b987..2767849e673 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, no-undef, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js index 083dc23c796..2c8ecba7de4 100644 --- a/app/assets/javascripts/snippet/snippet_bundle.js +++ b/app/assets/javascripts/snippet/snippet_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, no-undef, quotes, semi, padded-blocks, max-len */ /*= require_tree . */ (function() { diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index cfd1e2204d5..32803fa790b 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, no-undef, padded-blocks, max-len */ (function() { this.Star = (function() { function Star() { diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js index f9915593657..6d75688deeb 100644 --- a/app/assets/javascripts/subscription.js +++ b/app/assets/javascripts/subscription.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, one-var, one-var-declaration-per-line, camelcase, consistent-return, no-undef, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js index 2ca65cb762d..185d20775d0 100644 --- a/app/assets/javascripts/subscription_select.js +++ b/app/assets/javascripts/subscription_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */ (function() { this.SubscriptionSelect = (function() { function SubscriptionSelect() { diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js index 77ad4f30b7a..bd37d69165f 100644 --- a/app/assets/javascripts/syntax_highlight.js +++ b/app/assets/javascripts/syntax_highlight.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */ // Syntax Highlighter // // Applies a syntax highlighting color scheme CSS class to any element with the diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js index 70aff4b9a2f..54c473d936d 100644 --- a/app/assets/javascripts/tree.js +++ b/app/assets/javascripts/tree.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, padded-blocks, max-len */ (function() { this.TreeView = (function() { function TreeView() { diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js index 35f2b1e2b25..5d991542b51 100644 --- a/app/assets/javascripts/u2f/authenticate.js +++ b/app/assets/javascripts/u2f/authenticate.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-undef, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */ // Authenticate U2F (universal 2nd factor) devices for users to authenticate with. // // State Flow #1: setup -> in_progress -> authenticated -> POST to server diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js index aff605169e4..4c70a6e9bb6 100644 --- a/app/assets/javascripts/u2f/error.js +++ b/app/assets/javascripts/u2f/error.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, no-undef, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js index 22fbf9f3a91..97d8993cac2 100644 --- a/app/assets/javascripts/u2f/register.js +++ b/app/assets/javascripts/u2f/register.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */ // Register U2F (universal 2nd factor) devices for users to authenticate with. // // State Flow #1: setup -> in_progress -> registered -> POST to server diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js index 2eab2d5ae23..eedd3bcd5a1 100644 --- a/app/assets/javascripts/u2f/util.js +++ b/app/assets/javascripts/u2f/util.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */ (function() { this.U2FUtil = (function() { function U2FUtil() {} diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js index 0ec878e7e60..6d739039a5b 100644 --- a/app/assets/javascripts/users/calendar.js +++ b/app/assets/javascripts/users/calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, vars-on-top, semi, keyword-spacing, no-plusplus, no-undef, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js index 22bee0f6187..f50802bdf2e 100644 --- a/app/assets/javascripts/users/users_bundle.js +++ b/app/assets/javascripts/users/users_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require_tree . */ diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 7a2221dbaf5..c6e18fad832 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, no-undef, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-plusplus, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, keyword-spacing, no-param-reassign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, slice = [].slice; diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js index ad9b842db3c..5dd853389c2 100644 --- a/app/assets/javascripts/wikis.js +++ b/app/assets/javascripts/wikis.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, consistent-return, one-var, one-var-declaration-per-line, no-undef, prefer-template, padded-blocks, max-len */ /*= require latinise */ diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js index fa124e7052d..82eb761442a 100644 --- a/app/assets/javascripts/zen_mode.js +++ b/app/assets/javascripts/zen_mode.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, no-undef, camelcase, comma-dangle, padded-blocks, max-len */ // Zen Mode (full screen) textarea // /*= provides zen_mode:enter */ diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index d5cca1b10fb..7c7f991dd87 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -39,3 +39,5 @@ @import "framework/typography.scss"; @import "framework/zen.scss"; @import "framework/blank"; +@import "framework/wells.scss"; +@import "framework/page-header.scss"; diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 9acff45de75..4a9aa0f8717 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -349,6 +349,12 @@ } } +.btn-inverted { + &-secondary { + @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light); + } +} + @media (max-width: $screen-xs-max) { .btn-wide-on-xs { width: 100%; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index ad5ac589d0f..7f5583c917a 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -376,3 +376,19 @@ table { } .hide-bottom-border { border-bottom: none !important; } + +.gl-accessibility { + &:focus { + top: 1px; + left: 1px; + width: auto; + height: 100%; + line-height: 50px; + padding: 0 10px; + clip: auto; + text-decoration: none; + color: $gl-title-color; + background: $gray-light; + z-index: 1; + } +} diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 07c8874bf03..909a0f4afda 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -11,7 +11,7 @@ border-radius: 0; font-family: $monospace_font; font-size: $code_font_size; - line-height: $code_line_height !important; + line-height: 19px; margin: 0; overflow: auto; overflow-y: hidden; @@ -47,7 +47,7 @@ font-family: $monospace_font; display: block; font-size: $code_font_size !important; - line-height: $code_line_height !important; + line-height: 19px; white-space: nowrap; i { diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss new file mode 100644 index 00000000000..85c1385d5d9 --- /dev/null +++ b/app/assets/stylesheets/framework/page-header.scss @@ -0,0 +1,67 @@ +.page-content-header { + line-height: 34px; + padding: 10px 0; + margin-bottom: 0; + + @media (min-width: $screen-sm-min) { + display: flex; + align-items: center; + + .header-main-content { + flex: 1; + } + } + + .header-action-buttons { + i { + color: $gl-icon-color; + font-size: 13px; + margin-right: 3px; + } + + @media (max-width: $screen-xs-max) { + .btn { + width: 100%; + margin-top: 10px; + } + + .dropdown { + width: 100%; + } + } + } + + .avatar { + @extend .avatar-inline; + margin-left: 0; + + @media (min-width: $screen-sm-min) { + margin-left: 4px; + } + } + + .commit-committer-link, + .commit-author-link { + color: $gl-gray; + font-weight: bold; + } + + .fa-clipboard { + color: $dropdown-title-btn-color; + } + + .commit-info { + &.branches { + margin-left: 8px; + } + } + + .ci-status-link { + + svg { + position: relative; + top: 2px; + margin: 0 2px 0 3px; + } + } +} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 8bf5edfde50..92226f7432e 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -90,8 +90,8 @@ $table-border-color: #f0f0f0; $background-color: $gray-light; $dark-background-color: #f5f5f5; $table-text-gray: #8f8f8f; -$widget-expand-item: #e8f2f7; -$widget-inner-border: #eef0f2; +$well-expand-item: #e8f2f7; +$well-inner-border: #eef0f2; /* * Text diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss new file mode 100644 index 00000000000..192939f4527 --- /dev/null +++ b/app/assets/stylesheets/framework/wells.scss @@ -0,0 +1,45 @@ +.info-well { + background: $background-color; + color: $gl-gray; + border: 1px solid $border-color; + border-radius: $border-radius-default; + + .well-segment { + padding: $gl-padding; + + &:not(:last-of-type) { + border-bottom: 1px solid $well-inner-border; + } + + &.branch-info { + .monospace, + .commit-info { + margin-left: 4px; + } + } + } + + .icon-container { + display: inline-block; + margin-right: 8px; + + svg { + position: relative; + top: 2px; + height: 16px; + width: 16px; + } + + &.commit-icon { + svg { + path { + fill: $gl-text-color; + } + } + } + } + + .label.label-gray { + background-color: $well-expand-item; + } +} diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss index 6cefafd8fc7..14812e171fd 100644 --- a/app/assets/stylesheets/pages/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -160,3 +160,9 @@ } } } + +.admin-builds-table { + .ci-table td:last-child { + min-width: 120px; + } +} diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index f1d311cabbe..48f11eb2552 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -40,6 +40,19 @@ margin-bottom: 10px; } } + + .environment-information { + background-color: $background-color; + border: 1px solid $border-color; + padding: 12px $gl-padding; + border-radius: $border-radius-default; + + svg { + position: relative; + top: 1px; + margin-right: 5px; + } + } } .build-header { @@ -49,10 +62,6 @@ min-height: 58px; align-items: center; - .btn-inverted { - @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light); - } - @media (max-width: $screen-sm-max) { padding-right: 40px; @@ -63,14 +72,14 @@ .header-content { flex: 1; - } - a { - color: $gl-gray; + a { + color: $gl-gray; - &:hover { - color: $gl-link-color; - text-decoration: none; + &:hover { + color: $gl-link-color; + text-decoration: none; + } } } diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index 47d3e72679b..ddc9d0e2b1a 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -26,143 +26,12 @@ white-space: pre-wrap; } -.commit-info-row { - margin-bottom: 10px; - line-height: 24px; - padding-top: 6px; - - &.commit-info-row-header { - line-height: 34px; - padding: 10px 0; - margin-bottom: 0; - - @media (min-width: $screen-sm-min) { - display: flex; - align-items: center; - - .commit-meta { - flex: 1; - } - } - - .commit-hash-full { - @media (max-width: $screen-sm-max) { - width: 80px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: inline-block; - vertical-align: bottom; - } - } - - .commit-action-buttons { - i { - color: $gl-icon-color; - font-size: 13px; - margin-right: 3px; - } - - @media (max-width: $screen-xs-max) { - .dropdown { - width: 100%; - margin-top: 10px; - } - - .dropdown-toggle { - width: 100%; - } - } - } - } - - .avatar { - @extend .avatar-inline; - margin-left: 0; - - @media (min-width: $screen-sm-min) { - margin-left: 4px; - } - } - - .commit-committer-link, - .commit-author-link { - color: $gl-gray; - font-weight: bold; - } - - .fa-clipboard { - color: $dropdown-title-btn-color; - } - - .commit-info { - &.branches { - margin-left: 8px; - } - } - - .ci-status-link { - - svg { - position: relative; - top: 2px; - margin: 0 2px 0 3px; - } - } -} - .js-details-expand { &:hover { text-decoration: none; } } -.commit-info-widget { - background: $background-color; - color: $gl-gray; - border: 1px solid $border-color; - border-radius: $border-radius-default; - - .widget-row { - padding: $gl-padding; - - &:not(:last-of-type) { - border-bottom: 1px solid $widget-inner-border; - } - - &.branch-info { - .monospace, - .commit-info { - margin-left: 4px; - } - } - } - - .icon-container { - display: inline-block; - margin-right: 8px; - - svg { - position: relative; - top: 2px; - height: 16px; - width: 16px; - } - - &.commit-icon { - svg { - path { - fill: $gl-text-color; - } - } - } - } - - .label.label-gray { - background-color: $widget-expand-item; - } -} - .ci-status-link { svg { overflow: visible; @@ -184,6 +53,17 @@ } } +.commit-hash-full { + @media (max-width: $screen-sm-max) { + width: 80px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + vertical-align: bottom; + } +} + .file-stats { ul { list-style: none; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 3e7fc3fa52c..eb171195309 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -55,9 +55,28 @@ ul.related-merge-requests > li { } .merge-request-status { - color: $gl-gray; - font-size: 15px; - font-weight: bold; + font-size: 13px; + padding: 0 5px; + color: $white-light; + height: 20px; + border-radius: 3px; + line-height: 18px; + border: 1px solid; + + &.merged { + border-color: darken($blue-normal, 10%); + background: $blue-normal; + } + + &.closed { + border-color: darken($red-normal, 10%); + background: $red-normal; + } + + &.open { + border: 1px solid darken($green-normal, 10%); + background: $green-normal; + } } .merge-request, diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 397f89f501a..e39ce19f846 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -90,7 +90,7 @@ @media (min-width: $screen-sm-min) { display: inline-block; - width: 40%; + width: 30%; margin-left: 10px; margin-bottom: 0; vertical-align: middle; @@ -222,6 +222,14 @@ width: 100%; } +.label-subscription { + vertical-align: middle; + + .dropdown-group-label a { + cursor: pointer; + } +} + .label-subscribe-button { .label-subscribe-button-icon { &[disabled] { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 6cf43713fec..b6a82460f25 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -61,7 +61,7 @@ } .ci_widget { - border-bottom: 1px solid $widget-inner-border; + border-bottom: 1px solid $well-inner-border; svg { margin-right: 4px; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 9bfa1c96a5d..cc16d83c210 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -35,11 +35,83 @@ ul.notes { .system-note { font-size: 14px; - padding-top: 10px; - padding-bottom: 10px; - background: #fdfdfd; + padding: 0; + clear: both; + + &.timeline-entry::after { + clear: none; + } + + .system-note-message { + text-transform: lowercase; + + a { + color: $gl-link-color; + text-decoration: none; + } + } + + .timeline-content { + padding: 14px 10px; + } + + .note-body { + overflow: hidden; + + .system-note-commit-list-toggler { + display: none; + padding: 10px 0 0; + cursor: pointer; + } + + .note-text { + & p:first-child { + display: none; + } + + &.system-note-commit-list { + max-height: 63px; + overflow: hidden; + display: block; + + ul { + margin: 3px 0 3px 15px !important; + + li { + font-family: $monospace_font; + font-size: 12px; + } + } + + p:first-child { + display: none; + } + + &::after { + content: ''; + width: 100%; + height: 20px; + position: absolute; + left: 0; + bottom: 50px; + background: linear-gradient(rgba($gray-light, .3) 0, $white-light); + } + + &.hide-shade { + max-height: 100%; + overflow: auto; + + &::after { + background: transparent; + } + } + } + } + } .timeline-icon { + display: none; + .avatar { visibility: hidden; @@ -65,6 +137,12 @@ ul.notes { position: relative; border-bottom: 1px solid $table-border-gray; + &.note-discussion { + &.timeline-entry { + padding: 14px 10px; + } + } + &.is-editting { .note-header, .note-text, @@ -88,10 +166,8 @@ ul.notes { overflow: auto; word-wrap: break-word; @include md-typography; - // Reset ul style types since we're nested inside a ul already @include bulleted-list; - ul.task-list { ul:not(.task-list) { padding-left: 1.3em; @@ -111,6 +187,11 @@ ul.notes { padding-bottom: 3px; padding-right: 20px; + p { + display: inline; + margin: 0; + } + @media (min-width: $screen-sm-min) { padding-right: 0; } @@ -238,6 +319,10 @@ ul.notes { } } +.discussion-header { + font-size: 14px; +} + .note-headline-light, .discussion-headline-light { color: $notes-light-color; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 881621a2655..a44a496c728 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -300,6 +300,8 @@ .pipeline-graph { width: 100%; + background-color: $background-color; + padding: $gl-padding; overflow: auto; white-space: nowrap; transition: max-height 0.3s, padding 0.3s; @@ -363,6 +365,7 @@ .build { border: 1px solid $border-color; + background-color: $white-light; position: relative; padding: 7px 10px 8px; border-radius: 30px; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index e9ba2e5c098..63d0a34e610 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -8,6 +8,10 @@ border-bottom: none; } } + + .blob-result { + margin: 5px 0; + } } .search { diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index daa82336208..5c44637fdee 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -55,7 +55,13 @@ class AutocompleteController < ApplicationController def find_users @users = if @project - @project.team.users + user_ids = @project.team.users.pluck(:id) + + if params[:author_id].present? + user_ids << params[:author_id] + end + + User.where(id: user_ids) elsif params[:group_id].present? group = Group.find(params[:group_id]) return render_404 unless can?(current_user, :read_group, group) diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb new file mode 100644 index 00000000000..2aaf8f2b451 --- /dev/null +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -0,0 +1,7 @@ +module CycleAnalyticsParams + extend ActiveSupport::Concern + + def start_date(params) + params[:start_date] == '30' ? 30.days.ago : 90.days.ago + end +end diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index b5e79099e39..6247934f81e 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -10,11 +10,11 @@ module IssuableCollections private def issues_collection - issues_finder.execute + issues_finder.execute.preload(:project, :author, :assignee, :labels, :milestone, project: :namespace) end def merge_requests_collection - merge_requests_finder.execute + merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :merge_request_diff, target_project: :namespace) end def issues_finder diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index b89fb94be6e..b46adcceb60 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -7,7 +7,6 @@ module IssuesAction @issues = issues_collection .non_archived - .preload(:author, :project) .page(params[:page]) respond_to do |format| diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index a1b0eee37f9..6546a07b41c 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -7,7 +7,6 @@ module MergeRequestsAction @merge_requests = merge_requests_collection .non_archived - .preload(:author, :target_project) .page(params[:page]) end end diff --git a/app/controllers/concerns/toggle_subscription_action.rb b/app/controllers/concerns/toggle_subscription_action.rb index 9e3b9be2ff4..92cb534343e 100644 --- a/app/controllers/concerns/toggle_subscription_action.rb +++ b/app/controllers/concerns/toggle_subscription_action.rb @@ -4,13 +4,17 @@ module ToggleSubscriptionAction def toggle_subscription return unless current_user - subscribable_resource.toggle_subscription(current_user) + subscribable_resource.toggle_subscription(current_user, subscribable_project) head :ok end private + def subscribable_project + @project || raise(NotImplementedError) + end + def subscribable_resource raise NotImplementedError end diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index 29528b2cfaa..587898a8634 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -1,4 +1,6 @@ class Groups::LabelsController < Groups::ApplicationController + include ToggleSubscriptionAction + before_action :label, only: [:edit, :update, :destroy] before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy] before_action :save_previous_label_path, only: [:edit] @@ -69,6 +71,11 @@ class Groups::LabelsController < Groups::ApplicationController def label @label ||= @group.labels.find(params[:id]) end + alias_method :subscribable_resource, :label + + def subscribable_project + nil + end def label_params params.require(:label).permit(:title, :description, :color) diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb new file mode 100644 index 00000000000..6a1f468ba5a --- /dev/null +++ b/app/controllers/profiles/chat_names_controller.rb @@ -0,0 +1,64 @@ +class Profiles::ChatNamesController < Profiles::ApplicationController + before_action :chat_name_token, only: [:new] + before_action :chat_name_params, only: [:new, :create, :deny] + + def index + @chat_names = current_user.chat_names + end + + def new + end + + def create + new_chat_name = current_user.chat_names.new(chat_name_params) + + if new_chat_name.save + flash[:notice] = "Authorized #{new_chat_name.chat_name}" + else + flash[:alert] = "Could not authorize chat nickname. Try again!" + end + + delete_chat_name_token + redirect_to profile_chat_names_path + end + + def deny + delete_chat_name_token + + flash[:notice] = "Denied authorization of chat nickname #{chat_name_params[:user_name]}." + + redirect_to profile_chat_names_path + end + + def destroy + @chat_name = chat_names.find(params[:id]) + + if @chat_name.destroy + flash[:notice] = "Deleted chat nickname: #{@chat_name.chat_name}!" + else + flash[:alert] = "Could not delete chat nickname #{@chat_name.chat_name}." + end + + redirect_to profile_chat_names_path + end + + private + + def delete_chat_name_token + chat_name_token.delete + end + + def chat_name_params + @chat_name_params ||= chat_name_token.get || render_404 + end + + def chat_name_token + return render_404 unless params[:token] || render_404 + + @chat_name_token ||= Gitlab::ChatNameToken.new(params[:token]) + end + + def chat_names + @chat_names ||= current_user.chat_names + end +end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index b78cc6585ba..56ced786311 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -42,7 +42,7 @@ class Projects::BlobController < Projects::ApplicationController after_edit_path = if from_merge_request && @target_branch == @ref diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + - "#file-path-#{hexdigest(@path)}" + "##{hexdigest(@path)}" else namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path)) end diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb new file mode 100644 index 00000000000..13b3eec761f --- /dev/null +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -0,0 +1,65 @@ +module Projects + module CycleAnalytics + class EventsController < Projects::ApplicationController + include CycleAnalyticsParams + + before_action :authorize_read_cycle_analytics! + before_action :authorize_read_build!, only: [:test, :staging] + before_action :authorize_read_issue!, only: [:issue, :production] + before_action :authorize_read_merge_request!, only: [:code, :review] + + def issue + render_events(events.issue_events) + end + + def plan + render_events(events.plan_events) + end + + def code + render_events(events.code_events) + end + + def test + options[:branch] = events_params[:branch_name] + + render_events(events.test_events) + end + + def review + render_events(events.review_events) + end + + def staging + render_events(events.staging_events) + end + + def production + render_events(events.production_events) + end + + private + + def render_events(events_list) + respond_to do |format| + format.html + format.json { render json: { events: events_list } } + end + end + + def events + @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) + end + + def options + @options ||= { from: start_date(events_params), current_user: current_user } + end + + def events_params + return {} unless params[:events].present? + + params[:events].slice(:start_date, :branch_name) + end + end + end +end diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 16a7b1fc6e2..96eb75a0547 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -1,11 +1,12 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController include ActionView::Helpers::DateHelper include ActionView::Helpers::TextHelper + include CycleAnalyticsParams before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = CycleAnalytics.new(@project, from: parse_start_date) + @cycle_analytics = ::CycleAnalytics.new(@project, from: start_date(cycle_analytics_params)) respond_to do |format| format.html @@ -15,14 +16,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController private - def parse_start_date - case cycle_analytics_params[:start_date] - when '30' then 30.days.ago - when '90' then 90.days.ago - else 90.days.ago - end - end - def cycle_analytics_params return {} unless params[:cycle_analytics].present? diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index ade01c706a7..ba46e2528e6 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -4,6 +4,7 @@ class Projects::ForksController < Projects::ApplicationController # Authorize before_action :require_non_empty_project before_action :authorize_download_code! + before_action :authenticate_user!, only: [:new, :create] def index base_query = project.forks.includes(:creator) diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 42fd09e9b7e..824ed7be73e 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -3,7 +3,7 @@ class Projects::LabelsController < Projects::ApplicationController before_action :module_enabled before_action :label, only: [:edit, :update, :destroy] - before_action :find_labels, only: [:index, :set_priorities, :remove_priority] + before_action :find_labels, only: [:index, :set_priorities, :remove_priority, :toggle_subscription] before_action :authorize_read_label! before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :generate, :destroy, :remove_priority, @@ -123,7 +123,10 @@ class Projects::LabelsController < Projects::ApplicationController def label @label ||= @project.labels.find(params[:id]) end - alias_method :subscribable_resource, :label + + def subscribable_resource + @available_labels.find(params[:id]) + end def find_labels @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index dff0213411c..036fde87619 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -38,7 +38,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController def index @merge_requests = merge_requests_collection @merge_requests = @merge_requests.page(params[:page]) - @merge_requests = @merge_requests.preload(:target_project) if params[:label_name].present? labels_params = { project_id: @project.id, title: params[:label_name] } diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 97e6e9471e0..30c2a5d9982 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -10,8 +10,7 @@ class Projects::ServicesController < Projects::ApplicationController layout "project_settings" def index - @project.build_missing_services - @services = @project.services.visible.reload + @services = @project.find_or_initialize_services end def edit @@ -29,6 +28,8 @@ class Projects::ServicesController < Projects::ApplicationController end def test + return render_404 unless @service.can_test? + data = @service.test_data(project, current_user) outcome = @service.test(data) @@ -46,6 +47,6 @@ class Projects::ServicesController < Projects::ApplicationController private def service - @service ||= @project.services.find { |service| service.to_param == params[:id] } + @service ||= @project.find_or_initialize_service(params[:id]) end end diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb index 3085ff33aba..04c36b3ebfe 100644 --- a/app/controllers/sent_notifications_controller.rb +++ b/app/controllers/sent_notifications_controller.rb @@ -12,7 +12,7 @@ class SentNotificationsController < ApplicationController def unsubscribe_and_redirect noteable = @sent_notification.noteable - noteable.unsubscribe(@sent_notification.recipient) + noteable.unsubscribe(@sent_notification.recipient, @sent_notification.project) flash[:notice] = "You have been unsubscribed from this thread." diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index c4508ccc3b9..6e29f1e8a65 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -86,7 +86,7 @@ class UsersController < ApplicationController end def exists - render json: { exists: Namespace.where(path: params[:username].downcase).any? } + render json: { exists: !!Namespace.find_by_path_or_name(params[:username]) } end private diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 865f093f04a..fa0e2a5e3d8 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -6,7 +6,7 @@ class LabelsFinder < UnionFinder def execute(skip_authorization: false) @skip_authorization = skip_authorization - items = find_union(label_ids, Label) + items = find_union(label_ids, Label) || Label.none items = with_title(items) sort(items) end @@ -18,9 +18,11 @@ class LabelsFinder < UnionFinder def label_ids label_ids = [] - if project - label_ids << project.group.labels if project.group.present? - label_ids << project.labels + if project? + if project + label_ids << project.group.labels if project.group.present? + label_ids << project.labels + end else label_ids << Label.where(group_id: projects.group_ids) label_ids << Label.where(project_id: projects.select(:id)) @@ -40,16 +42,16 @@ class LabelsFinder < UnionFinder items.where(title: title) end - def group_id - params[:group_id].presence + def group? + params[:group_id].present? end - def project_id - params[:project_id].presence + def project? + params[:project_id].present? end - def projects_ids - params[:project_ids] + def projects? + params[:project_ids].present? end def title @@ -59,8 +61,9 @@ class LabelsFinder < UnionFinder def project return @project if defined?(@project) - if project_id - @project = find_project + if project? + @project = Project.find(params[:project_id]) + @project = nil unless authorized_to_read_labels?(@project) else @project = nil end @@ -68,26 +71,20 @@ class LabelsFinder < UnionFinder @project end - def find_project - if skip_authorization - Project.find_by(id: project_id) - else - available_projects.find_by(id: project_id) - end - end - def projects return @projects if defined?(@projects) - @projects = skip_authorization ? Project.all : available_projects - @projects = @projects.in_namespace(group_id) if group_id - @projects = @projects.where(id: projects_ids) if projects_ids + @projects = skip_authorization ? Project.all : ProjectsFinder.new.execute(current_user) + @projects = @projects.in_namespace(params[:group_id]) if group? + @projects = @projects.where(id: params[:project_ids]) if projects? @projects = @projects.reorder(nil) @projects end - def available_projects - @available_projects ||= ProjectsFinder.new.execute(current_user) + def authorized_to_read_labels?(project) + return true if skip_authorization + + Ability.allowed?(current_user, :read_label, project) end end diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb new file mode 100644 index 00000000000..27975b7ddb7 --- /dev/null +++ b/app/helpers/environment_helper.rb @@ -0,0 +1,29 @@ +module EnvironmentHelper + def environment_for_build(project, build) + return unless build.environment + + project.environments.find_by(name: build.expanded_environment_name) + end + + def environment_link_for_build(project, build) + environment = environment_for_build(project, build) + if environment + link_to environment.name, namespace_project_environment_path(project.namespace, project, environment) + else + content_tag :span, build.expanded_environment_name + end + end + + def deployment_link(deployment) + return unless deployment + + link_to "##{deployment.iid}", [deployment.project.namespace.becomes(Namespace), deployment.project, deployment.deployable] + end + + def last_deployment_link_for_environment_build(project, build) + environment = environment_for_build(project, build) + return unless environment + + deployment_link(environment.last_deployment) + end +end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 00e64076408..f1a0b929d82 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -86,7 +86,7 @@ module EventsHelper elsif event.merge_request? namespace_project_merge_request_url(event.project.namespace, event.project, event.merge_request) - elsif event.note? && event.commit_note? + elsif event.commit_note? namespace_project_commit_url(event.project.namespace, event.project, event.note_target) elsif event.note? @@ -127,7 +127,7 @@ module EventsHelper end def event_note_target_path(event) - if event.note? && event.commit_note? + if event.commit_note? namespace_project_commit_path(event.project.namespace, event.project, event.note_target, diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 8b97cf07fb9..8bebda07787 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -136,8 +136,19 @@ module IssuablesHelper html.html_safe end + def cached_assigned_issuables_count(assignee, issuable_type, state) + cache_key = hexdigest(['assigned_issuables_count', assignee.id, issuable_type, state].join('-')) + Rails.cache.fetch(cache_key, expires_in: 2.minutes) do + assigned_issuables_count(assignee, issuable_type, state) + end + end + private + def assigned_issuables_count(assignee, issuable_type, state) + assignee.public_send("assigned_#{issuable_type}").public_send(state).count + end + def sidebar_gutter_collapsed? cookies[:collapsed_gutter] == 'true' end @@ -160,9 +171,11 @@ module IssuablesHelper def issuables_count_for_state(issuable_type, state) issuables_finder = public_send("#{issuable_type}_finder") - issuables_finder.params[:state] = state + + params = issuables_finder.params.merge(state: state) + finder = issuables_finder.class.new(issuables_finder.current_user, params) - issuables_finder.execute.page(1).total_count + finder.execute.page(1).total_count end IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page] diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 221a84b042f..4f180456b16 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -68,14 +68,6 @@ module LabelsHelper end end - def toggle_subscription_data(label) - return unless label.is_a?(ProjectLabel) - - { - url: toggle_subscription_namespace_project_label_path(label.project.namespace, label.project, label) - } - end - def render_colored_label(label, label_suffix = '', tooltip: true) label_color = label.color || Label::DEFAULT_COLOR text_color = text_color_for_bg(label_color) @@ -148,20 +140,24 @@ module LabelsHelper end end - def label_subscription_status(label) - case label - when GroupLabel then 'Subscribing to group labels is currently not supported.' - when ProjectLabel then label.subscribed?(current_user) ? 'subscribed' : 'unsubscribed' - end + def label_subscription_status(label, project) + return 'project-level' if label.subscribed?(current_user, project) + return 'group-level' if label.subscribed?(current_user) + + 'unsubscribed' end - def label_subscription_toggle_button_text(label) - case label - when GroupLabel then 'Subscribing to group labels is currently not supported.' - when ProjectLabel then label.subscribed?(current_user) ? 'Unsubscribe' : 'Subscribe' + def group_label_unsubscribe_path(label, project) + case label_subscription_status(label, project) + when 'project-level' then toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) + when 'group-level' then toggle_subscription_group_label_path(label.group, label) end end + def label_subscription_toggle_button_text(label, project) + label.subscribed?(current_user, project) ? 'Unsubscribe' : 'Subscribe' + end + def label_deletion_confirm_text(label) case label when GroupLabel then 'Remove this label? This will affect all projects within the group. Are you sure?' diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index a46f2c6e17d..6e68aad4cb7 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -50,7 +50,7 @@ module PreferencesHelper end def default_project_view - return 'readme' unless current_user + return anonymous_project_view unless current_user user_view = current_user.project_view @@ -66,4 +66,8 @@ module PreferencesHelper "customize_workflow" end end + + def anonymous_project_view + @project.empty_repo? || !can?(current_user, :download_code, @project) ? 'activity' : 'readme' + end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index aba3a3f9c5d..cdb9663877c 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -31,34 +31,7 @@ module SearchHelper end def parse_search_result(result) - ref = nil - filename = nil - basename = nil - startline = 0 - - result.each_line.each_with_index do |line, index| - if line =~ /^.*:.*:\d+:/ - ref, filename, startline = line.split(':') - startline = startline.to_i - index - extname = Regexp.escape(File.extname(filename)) - basename = filename.sub(/#{extname}$/, '') - break - end - end - - data = "" - - result.each_line do |line| - data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') - end - - OpenStruct.new( - filename: filename, - basename: basename, - ref: ref, - startline: startline, - data: data - ) + Gitlab::ProjectSearchResults.parse_search_result(result) end private diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb index c41181bab3d..b0135ea2e95 100644 --- a/app/helpers/triggers_helper.rb +++ b/app/helpers/triggers_helper.rb @@ -6,4 +6,8 @@ module TriggersHelper "#{Settings.gitlab.url}/api/v3/projects/#{project_id}/ref/#{ref}/trigger/builds" end end + + def service_trigger_url(service) + "#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger" + end end diff --git a/app/models/chat_name.rb b/app/models/chat_name.rb new file mode 100644 index 00000000000..f321db75eeb --- /dev/null +++ b/app/models/chat_name.rb @@ -0,0 +1,12 @@ +class ChatName < ActiveRecord::Base + belongs_to :service + belongs_to :user + + validates :user, presence: true + validates :service, presence: true + validates :team_id, presence: true + validates :chat_id, presence: true + + validates :user_id, uniqueness: { scope: [:service_id] } + validates :chat_id, uniqueness: { scope: [:service_id, :team_id] } +end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index bf5f92f8462..5d2e7d94190 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -7,6 +7,8 @@ module Ci belongs_to :trigger_request belongs_to :erased_by, class_name: 'User' + has_many :deployments, as: :deployable + serialize :options serialize :yaml_variables @@ -125,6 +127,34 @@ module Ci !self.pipeline.statuses.latest.include?(self) end + def expanded_environment_name + ExpandVariables.expand(environment, variables) if environment + end + + def has_environment? + self.environment.present? + end + + def starts_environment? + has_environment? && self.environment_action == 'start' + end + + def stops_environment? + has_environment? && self.environment_action == 'stop' + end + + def environment_action + self.options.fetch(:environment, {}).fetch(:action, 'start') + end + + def outdated_deployment? + success? && !last_deployment.try(:last?) + end + + def last_deployment + deployments.last + end + def depends_on_builds # Get builds of the same type latest_builds = self.pipeline.builds.latest @@ -271,6 +301,7 @@ module Ci def append_trace(trace_part, offset) recreate_trace_dir + touch if needs_touch? trace_part = hide_secrets(trace_part) @@ -280,6 +311,10 @@ module Ci end end + def needs_touch? + Time.now - updated_at > 15.minutes.to_i + end + def trace_file_path if has_old_trace_file? old_path_to_trace diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 664bb594aa9..ec9e7e5ae2b 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -215,7 +215,7 @@ module Issuable end end - def subscribed_without_subscriptions?(user) + def subscribed_without_subscriptions?(user, project) participants(user).include?(user) end diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb index 083257f1005..83daa9b1a64 100644 --- a/app/models/concerns/subscribable.rb +++ b/app/models/concerns/subscribable.rb @@ -12,39 +12,71 @@ module Subscribable has_many :subscriptions, dependent: :destroy, as: :subscribable end - def subscribed?(user) - if subscription = subscriptions.find_by_user_id(user.id) + def subscribed?(user, project = nil) + if subscription = subscriptions.find_by(user: user, project: project) subscription.subscribed else - subscribed_without_subscriptions?(user) + subscribed_without_subscriptions?(user, project) end end # Override this method to define custom logic to consider a subscribable as # subscribed without an explicit subscription record. - def subscribed_without_subscriptions?(user) + def subscribed_without_subscriptions?(user, project) false end - def subscribers - subscriptions.where(subscribed: true).map(&:user) + def subscribers(project) + subscriptions_available(project). + where(subscribed: true). + map(&:user) end - def toggle_subscription(user) - subscriptions. - find_or_initialize_by(user_id: user.id). - update(subscribed: !subscribed?(user)) + def toggle_subscription(user, project = nil) + unsubscribe_from_other_levels(user, project) + + find_or_initialize_subscription(user, project). + update(subscribed: !subscribed?(user, project)) + end + + def subscribe(user, project = nil) + unsubscribe_from_other_levels(user, project) + + find_or_initialize_subscription(user, project) + .update(subscribed: true) + end + + def unsubscribe(user, project = nil) + unsubscribe_from_other_levels(user, project) + + find_or_initialize_subscription(user, project) + .update(subscribed: false) end - def subscribe(user) + private + + def unsubscribe_from_other_levels(user, project) + other_subscriptions = subscriptions.where(user: user) + + other_subscriptions = + if project.blank? + other_subscriptions.where.not(project: nil) + else + other_subscriptions.where(project: nil) + end + + other_subscriptions.update_all(subscribed: false) + end + + def find_or_initialize_subscription(user, project) subscriptions. - find_or_initialize_by(user_id: user.id). - update(subscribed: true) + find_or_initialize_by(user_id: user.id, project_id: project.try(:id)) end - def unsubscribe(user) + def subscriptions_available(project) + t = Subscription.arel_table + subscriptions. - find_or_initialize_by(user_id: user.id). - update(subscribed: false) + where(t[:project_id].eq(nil).or(t[:project_id].eq(project.try(:id)))) end end diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb index 8ed4a56b19b..314a1ce9b63 100644 --- a/app/models/cycle_analytics.rb +++ b/app/models/cycle_analytics.rb @@ -1,12 +1,8 @@ class CycleAnalytics - include Gitlab::Database::Median - include Gitlab::Database::DateTime - - DEPLOYMENT_METRIC_STAGES = %i[production staging] - def initialize(project, from:) @project = project @from = from + @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil) end def summary @@ -14,90 +10,46 @@ class CycleAnalytics end def issue - calculate_metric(:issue, + @fetcher.calculate_metric(:issue, Issue.arel_table[:created_at], [Issue::Metrics.arel_table[:first_associated_with_milestone_at], Issue::Metrics.arel_table[:first_added_to_board_at]]) end def plan - calculate_metric(:plan, + @fetcher.calculate_metric(:plan, [Issue::Metrics.arel_table[:first_associated_with_milestone_at], Issue::Metrics.arel_table[:first_added_to_board_at]], Issue::Metrics.arel_table[:first_mentioned_in_commit_at]) end def code - calculate_metric(:code, + @fetcher.calculate_metric(:code, Issue::Metrics.arel_table[:first_mentioned_in_commit_at], MergeRequest.arel_table[:created_at]) end def test - calculate_metric(:test, + @fetcher.calculate_metric(:test, MergeRequest::Metrics.arel_table[:latest_build_started_at], MergeRequest::Metrics.arel_table[:latest_build_finished_at]) end def review - calculate_metric(:review, + @fetcher.calculate_metric(:review, MergeRequest.arel_table[:created_at], MergeRequest::Metrics.arel_table[:merged_at]) end def staging - calculate_metric(:staging, + @fetcher.calculate_metric(:staging, MergeRequest::Metrics.arel_table[:merged_at], MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end def production - calculate_metric(:production, + @fetcher.calculate_metric(:production, Issue.arel_table[:created_at], MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end - - private - - def calculate_metric(name, start_time_attrs, end_time_attrs) - cte_table = Arel::Table.new("cte_table_for_#{name}") - - # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). - # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). - # We compute the (end_time - start_time) interval, and give it an alias based on the current - # cycle analytics stage. - interval_query = Arel::Nodes::As.new( - cte_table, - subtract_datetimes(base_query_for(name), end_time_attrs, start_time_attrs, name.to_s)) - - median_datetime(cte_table, interval_query, name) - end - - # Join table with a row for every <issue,merge_request> pair (where the merge request - # closes the given issue) with issue and merge request metrics included. The metrics - # are loaded with an inner join, so issues / merge requests without metrics are - # automatically excluded. - def base_query_for(name) - arel_table = MergeRequestsClosingIssues.arel_table - - # Load issues - query = arel_table.join(Issue.arel_table).on(Issue.arel_table[:id].eq(arel_table[:issue_id])). - join(Issue::Metrics.arel_table).on(Issue.arel_table[:id].eq(Issue::Metrics.arel_table[:issue_id])). - where(Issue.arel_table[:project_id].eq(@project.id)). - where(Issue.arel_table[:deleted_at].eq(nil)). - where(Issue.arel_table[:created_at].gteq(@from)) - - # Load merge_requests - query = query.join(MergeRequest.arel_table, Arel::Nodes::OuterJoin). - on(MergeRequest.arel_table[:id].eq(arel_table[:merge_request_id])). - join(MergeRequest::Metrics.arel_table). - on(MergeRequest.arel_table[:id].eq(MergeRequest::Metrics.arel_table[:merge_request_id])) - - if DEPLOYMENT_METRIC_STAGES.include?(name) - # Limit to merge requests that have been deployed to production after `@from` - query.where(MergeRequest::Metrics.arel_table[:first_deployed_to_production_at].gteq(@from)) - end - - query - end end diff --git a/app/models/environment.rb b/app/models/environment.rb index 73f415c0ef0..5278efd71d2 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -37,6 +37,10 @@ class Environment < ActiveRecord::Base state :stopped end + def recently_updated_on_branch?(ref) + ref.to_s == last_deployment.try(:ref) + end + def last_deployment deployments.last end @@ -92,6 +96,7 @@ class Environment < ActiveRecord::Base def stop!(current_user) return unless stoppable? + stop stop_action.play(current_user) end end diff --git a/app/models/event.rb b/app/models/event.rb index c76d88b1c7b..21eaca917b8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -62,7 +62,7 @@ class Event < ActiveRecord::Base end def visible_to_user?(user = nil) - if push? + if push? || commit_note? Ability.allowed?(user, :download_code, project) elsif membership_changed? true @@ -283,7 +283,7 @@ class Event < ActiveRecord::Base end def commit_note? - target.for_commit? + note? && target && target.for_commit? end def issue_note? @@ -295,7 +295,7 @@ class Event < ActiveRecord::Base end def project_snippet_note? - target.for_snippet? + note? && target && target.for_snippet? end def note_target diff --git a/app/models/issue.rb b/app/models/issue.rb index 4a4017003d8..6e8f5d3c422 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -266,7 +266,7 @@ class Issue < ActiveRecord::Base def as_json(options = {}) super(options).tap do |json| - json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) && options[:user] + json[:subscribed] = subscribed?(options[:user], project) if options.has_key?(:user) && options[:user] if options.has_key?(:labels) json[:labels] = labels.as_json( diff --git a/app/models/key.rb b/app/models/key.rb index 568a60b8af3..ff8dda2dc89 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -6,7 +6,7 @@ class Key < ActiveRecord::Base belongs_to :user - before_validation :strip_white_space, :generate_fingerprint + before_validation :generate_fingerprint validates :title, presence: true, length: { within: 0..255 } validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ } @@ -21,8 +21,9 @@ class Key < ActiveRecord::Base after_destroy :remove_from_shell after_destroy :post_destroy_hook - def strip_white_space - self.key = key.strip unless key.blank? + def key=(value) + value.strip! unless value.blank? + write_attribute(:key, value) end def publishable_key diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d76feb9680e..9d3eab52189 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -692,12 +692,15 @@ class MergeRequest < ActiveRecord::Base def environments return [] unless diff_head_commit - @environments ||= - begin - envs = target_project.environments_for(target_branch, diff_head_commit, with_tags: true) - envs.concat(source_project.environments_for(source_branch, diff_head_commit)) if source_project - envs.uniq - end + @environments ||= begin + target_envs = target_project.environments_for( + target_branch, commit: diff_head_commit, with_tags: true) + + source_envs = source_project.environments_for( + source_branch, commit: diff_head_commit) if source_project + + (target_envs.to_a + source_envs.to_a).uniq + end end def state_human_name diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index 99c49a020c9..cdc408738be 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -1,5 +1,6 @@ class MergeRequest::Metrics < ActiveRecord::Base belongs_to :merge_request + belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :pipeline_id def record! if merge_request.merged? && self.merged_at.blank? diff --git a/app/models/project.rb b/app/models/project.rb index bab2f0c53ca..34b44f90f1c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -23,7 +23,9 @@ class Project < ActiveRecord::Base cache_markdown_field :description, pipeline: :description - delegate :feature_available?, :builds_enabled?, :wiki_enabled?, :merge_requests_enabled?, to: :project_feature, allow_nil: true + delegate :feature_available?, :builds_enabled?, :wiki_enabled?, + :merge_requests_enabled?, :issues_enabled?, to: :project_feature, + allow_nil: true default_value_for :archived, false default_value_for :visibility_level, gitlab_config_features.visibility_level @@ -75,9 +77,9 @@ class Project < ActiveRecord::Base has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event' has_many :boards, before_add: :validate_board_limit, dependent: :destroy + has_many :chat_services # Project services - has_many :services has_one :campfire_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy @@ -90,6 +92,7 @@ class Project < ActiveRecord::Base has_one :assembla_service, dependent: :destroy has_one :asana_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy + has_one :mattermost_slash_commands_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy @@ -749,27 +752,32 @@ class Project < ActiveRecord::Base update_column(:has_external_wiki, services.external_wikis.any?) end - def build_missing_services + def find_or_initialize_services services_templates = Service.where(template: true) - Service.available_services_names.each do |service_name| + Service.available_services_names.map do |service_name| service = find_service(services, service_name) - # If service is available but missing in db - if service.nil? + if service + service + else # We should check if template for the service exists template = find_service(services_templates, service_name) if template.nil? - # If no template, we should create an instance. Ex `create_gitlab_ci_service` - public_send("create_#{service_name}_service") + # If no template, we should create an instance. Ex `build_gitlab_ci_service` + public_send("build_#{service_name}_service") else - Service.create_from_template(self.id, template) + Service.build_from_template(id, template) end end end end + def find_or_initialize_service(name) + find_or_initialize_services.find { |service| service.to_param == name } + end + def create_labels Label.templates.each do |label| params = label.attributes.except('id', 'template', 'created_at', 'updated_at') @@ -879,7 +887,7 @@ class Project < ActiveRecord::Base end def empty_repo? - !repository.exists? || !repository.has_visible_content? + repository.empty_repo? end def repo @@ -1319,22 +1327,30 @@ class Project < ActiveRecord::Base Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) } end - def environments_for(ref, commit, with_tags: false) - environment_ids = deployments.group(:environment_id). - select(:environment_id) + def environments_for(ref, commit: nil, with_tags: false) + deployments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' - environment_ids = - if with_tags - environment_ids.where('ref=? OR tag IS TRUE', ref) - else - environment_ids.where(ref: ref) - end + environment_ids = deployments + .where(deployments_query, ref.to_s) + .group(:environment_id) + .select(:environment_id) - environments.available.where(id: environment_ids).select do |environment| + environments_found = environments.available + .where(id: environment_ids).to_a + + return environments_found unless commit + + environments_found.select do |environment| environment.includes_commit?(commit) end end + def environments_recently_updated_on_branch(branch) + environments_for(branch).select do |environment| + environment.recently_updated_on_branch?(branch) + end + end + private def pushes_since_gc_redis_key diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 5c53c8f1ee5..03194fc2141 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -60,6 +60,10 @@ class ProjectFeature < ActiveRecord::Base merge_requests_access_level > DISABLED end + def issues_enabled? + issues_access_level > DISABLED + end + private # Validates builds and merge requests access level diff --git a/app/models/project_services/chat_service.rb b/app/models/project_services/chat_service.rb new file mode 100644 index 00000000000..d36beff5fa6 --- /dev/null +++ b/app/models/project_services/chat_service.rb @@ -0,0 +1,21 @@ +# Base class for Chat services +# This class is not meant to be used directly, but only to inherrit from. +class ChatService < Service + default_value_for :category, 'chat' + + has_many :chat_names, foreign_key: :service_id + + def valid_token?(token) + self.respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + end + + def supported_events + [] + end + + def trigger(params) + raise NotImplementedError + end +end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2dbe0075465..8915c06b633 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -70,7 +70,7 @@ class JiraService < IssueTrackerService end def jira_project - @jira_project ||= client.Project.find(project_key) + @jira_project ||= jira_request { client.Project.find(project_key) } end def help @@ -128,12 +128,19 @@ class JiraService < IssueTrackerService # we just want to test settings test_settings else - close_issue(push, issue) + jira_issue = jira_request { client.Issue.find(issue.iid) } + + return false unless jira_issue.present? + + close_issue(push, jira_issue) end end def create_cross_reference_note(mentioned, noteable, author) - issue_key = mentioned.id + jira_issue = jira_request { client.Issue.find(mentioned.id) } + + return false unless jira_issue.present? + project = self.project noteable_name = noteable.class.name.underscore.downcase noteable_id = if noteable.is_a?(Commit) @@ -160,7 +167,7 @@ class JiraService < IssueTrackerService } } - add_comment(data, issue_key) + add_comment(data, jira_issue) end # reason why service cannot be tested @@ -181,16 +188,14 @@ class JiraService < IssueTrackerService def test_settings return unless url.present? # Test settings by getting the project - jira_project - - rescue Errno::ECONNREFUSED, JIRA::HTTPError => e - Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{url}." - false + jira_request { jira_project.present? } end private def close_issue(entity, issue) + return if issue.nil? || issue.resolution.present? + commit_id = if entity.is_a?(Commit) entity.id elsif entity.is_a?(MergeRequest) @@ -200,55 +205,85 @@ class JiraService < IssueTrackerService commit_url = build_entity_url(:commit, commit_id) # Depending on the JIRA project's workflow, a comment during transition - # may or may not be allowed. Split the operation in to two calls so the - # comment always works. - transition_issue(issue) - add_issue_solved_comment(issue, commit_id, commit_url) + # may or may not be allowed. Refresh the issue after transition and check + # if it is closed, so we don't have one comment for every commit. + issue = jira_request { client.Issue.find(issue.key) } if transition_issue(issue) + add_issue_solved_comment(issue, commit_id, commit_url) if issue.resolution end def transition_issue(issue) - issue = client.Issue.find(issue.iid) issue.transitions.build.save(transition: { id: jira_issue_transition_id }) end def add_issue_solved_comment(issue, commit_id, commit_url) - comment = "Issue solved with [#{commit_id}|#{commit_url}]." - send_message(issue.iid, comment) + link_title = "GitLab: Solved by commit #{commit_id}." + comment = "Issue solved with [#{commit_id}|#{commit_url}]." + link_props = build_remote_link_props(url: commit_url, title: link_title, resolved: true) + send_message(issue, comment, link_props) end - def add_comment(data, issue_key) - user_name = data[:user][:name] - user_url = data[:user][:url] - entity_name = data[:entity][:name] - entity_url = data[:entity][:url] + def add_comment(data, issue) + user_name = data[:user][:name] + user_url = data[:user][:url] + entity_name = data[:entity][:name] + entity_url = data[:entity][:url] entity_title = data[:entity][:title] project_name = data[:project][:name] - message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'" + message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'" + link_title = "GitLab: Mentioned on #{entity_name} - #{entity_title}" + link_props = build_remote_link_props(url: entity_url, title: link_title) - unless comment_exists?(issue_key, message) - send_message(issue_key, message) + unless comment_exists?(issue, message) + send_message(issue, message, link_props) end end - def comment_exists?(issue_key, message) - comments = client.Issue.find(issue_key).comments - comments.map { |comment| comment.body.include?(message) }.any? + def comment_exists?(issue, message) + comments = jira_request { issue.comments } + + comments.present? && comments.any? { |comment| comment.body.include?(message) } end - def send_message(issue_key, message) + def send_message(issue, message, remote_link_props) return unless url.present? - issue = client.Issue.find(issue_key) + jira_request do + if issue.comments.build.save!(body: message) + remote_link = issue.remotelink.build + remote_link.save!(remote_link_props) + result_message = "#{self.class.name} SUCCESS: Successfully posted to #{url}." + end - if issue.comments.build.save!(body: message) - result_message = "#{self.class.name} SUCCESS: Successfully posted to #{url}." + Rails.logger.info(result_message) + result_message end + end - Rails.logger.info(result_message) - result_message - rescue URI::InvalidURIError, Errno::ECONNREFUSED, JIRA::HTTPError => e - Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}" + # Build remote link on JIRA properties + # Icons here must be available on WEB so JIRA can read the URL + # We are using a open word graphics icon which have LGPL license + def build_remote_link_props(url:, title:, resolved: false) + status = { + resolved: resolved + } + + if resolved + status[:icon] = { + title: 'Closed', + url16x16: 'http://www.openwebgraphics.com/resources/data/1768/16x16_apply.png' + } + end + + { + GlobalID: 'GitLab', + object: { + url: url, + title: title, + status: status, + icon: { title: 'GitLab', url16x16: 'https://gitlab.com/favicon.ico' } + } + } end def resource_url(resource) @@ -256,16 +291,23 @@ class JiraService < IssueTrackerService end def build_entity_url(entity_name, entity_id) - resource_url( - polymorphic_url( - [ - self.project.namespace.becomes(Namespace), - self.project, - entity_name - ], - id: entity_id, - routing_type: :path - ) + polymorphic_url( + [ + self.project.namespace.becomes(Namespace), + self.project, + entity_name + ], + id: entity_id, + host: Settings.gitlab.base_url ) end + + # Handle errors when doing JIRA API calls + def jira_request + yield + + rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError => e + Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}" + nil + end end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb new file mode 100644 index 00000000000..67902329593 --- /dev/null +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -0,0 +1,56 @@ +class MattermostSlashCommandsService < ChatService + include TriggersHelper + + prop_accessor :token + + def can_test? + false + end + + def title + 'Mattermost Command' + end + + def description + "Perform common operations on GitLab in Mattermost" + end + + def to_param + 'mattermost_slash_commands' + end + + def help + "This service allows you to use slash commands with your Mattermost installation.<br/> + To setup this Service you need to create a new <b>Slash commands</b> in your Mattermost integration panel.<br/> + <br/> + Create integration with URL #{service_trigger_url(self)} and enter the token below." + end + + def fields + [ + { type: 'text', name: 'token', placeholder: '' } + ] + end + + def trigger(params) + return nil unless valid_token?(params[:token]) + + user = find_chat_user(params) + unless user + url = authorize_chat_name_url(params) + return Mattermost::Presenter.authorize_chat_name(url) + end + + Gitlab::ChatCommands::Command.new(project, user, params).execute + end + + private + + def find_chat_user(params) + ChatNames::FindUserService.new(self, params).execute + end + + def authorize_chat_name_url(params) + ChatNames::AuthorizeUserService.new(self, params).execute + end +end diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb index 9e84e90f38c..797c5937f09 100644 --- a/app/models/project_services/slack_service/note_message.rb +++ b/app/models/project_services/slack_service/note_message.rb @@ -46,25 +46,25 @@ class SlackService commit_sha = commit[:id] commit_sha = Commit.truncate_sha(commit_sha) commented_on_message( - "[commit #{commit_sha}](#{@note_url})", + "commit #{commit_sha}", format_title(commit[:message])) end def create_issue_note(issue) commented_on_message( - "[issue ##{issue[:iid]}](#{@note_url})", + "issue ##{issue[:iid]}", format_title(issue[:title])) end def create_merge_note(merge_request) commented_on_message( - "[merge request !#{merge_request[:iid]}](#{@note_url})", + "merge request !#{merge_request[:iid]}", format_title(merge_request[:title])) end def create_snippet_note(snippet) commented_on_message( - "[snippet ##{snippet[:id]}](#{@note_url})", + "snippet ##{snippet[:id]}", format_title(snippet[:title])) end @@ -76,8 +76,8 @@ class SlackService "[#{@project_name}](#{@project_url})" end - def commented_on_message(target_link, title) - @message = "#{@user_name} commented on #{target_link} in #{project_link}: *#{title}*" + def commented_on_message(target, title) + @message = "#{@user_name} [commented on #{target}](#{@note_url}) in #{project_link}: *#{title}*" end end end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 46f70da2452..9db96347322 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -127,7 +127,7 @@ class ProjectWiki end def search_files(query) - repository.search_files(query, default_branch) + repository.search_files_by_content(query, default_branch) end def repository diff --git a/app/models/repository.rb b/app/models/repository.rb index 4282197faa5..31be06be50c 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -15,16 +15,6 @@ class Repository Gitlab.config.repositories.storages end - def self.remove_storage_from_path(repo_path) - storages.find do |_, storage_path| - if repo_path.start_with?(storage_path) - return repo_path.sub(storage_path, '') - end - end - - repo_path - end - def initialize(path_with_namespace, project) @path_with_namespace = path_with_namespace @project = project @@ -186,11 +176,18 @@ class Repository options = { message: message, tagger: user_to_committer(user) } if message - GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do - rugged.tags.create(tag_name, target, options) + rugged.tags.create(tag_name, target, options) + tag = find_tag(tag_name) + + GitHooksService.new.execute(user, path_to_repo, oldrev, tag.target, ref) do + # we already created a tag, because we need tag SHA to pass correct + # values to hooks end - find_tag(tag_name) + tag + rescue GitHooksService::PreReceiveError + rugged.tags.delete(tag_name) + raise end def rm_branch(user, branch_name) @@ -1063,16 +1060,25 @@ class Repository merge_base(ancestor_id, descendant_id) == ancestor_id end - def search_files(query, ref) - unless exists? && has_visible_content? && query.present? - return [] - end + def empty_repo? + !exists? || !has_visible_content? + end + + def search_files_by_content(query, ref) + return [] if empty_repo? || query.blank? offset = 2 args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end + def search_files_by_name(query, ref) + return [] if empty_repo? || query.blank? + + args = %W(#{Gitlab.config.git.bin_path} ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)}) + Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip) + end + def fetch_ref(source_path, source_ref, target_ref) args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) Gitlab::Popen.popen(args, path_to_repo) diff --git a/app/models/service.rb b/app/models/service.rb index 625fbc48302..edd6b5329f3 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -202,7 +202,6 @@ class Service < ActiveRecord::Base bamboo buildkite builds_email - pipelines_email bugzilla campfire custom_issue_tracker @@ -214,6 +213,8 @@ class Service < ActiveRecord::Base hipchat irker jira + mattermost_slash_commands + pipelines_email pivotaltracker pushover redmine @@ -222,11 +223,11 @@ class Service < ActiveRecord::Base ] end - def self.create_from_template(project_id, template) + def self.build_from_template(project_id, template) service = template.dup service.template = false service.project_id = project_id - service if service.save + service end private diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 3b8aa1eb866..17869c8bac2 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,8 +1,9 @@ class Subscription < ActiveRecord::Base belongs_to :user + belongs_to :project belongs_to :subscribable, polymorphic: true - validates :user_id, - uniqueness: { scope: [:subscribable_id, :subscribable_type] }, - presence: true + validates :user, :subscribable, presence: true + + validates :project_id, uniqueness: { scope: [:subscribable_id, :subscribable_type, :user_id] } end diff --git a/app/models/user.rb b/app/models/user.rb index 3813df6684e..519ed92e28b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -56,6 +56,7 @@ class User < ActiveRecord::Base has_many :personal_access_tokens, dependent: :destroy has_many :identities, dependent: :destroy, autosave: true has_many :u2f_registrations, dependent: :destroy + has_many :chat_names, dependent: :destroy # Groups has_many :members, dependent: :destroy @@ -173,7 +174,7 @@ class User < ActiveRecord::Base scope :external, -> { where(external: true) } scope :active, -> { with_state(:active) } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } - scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } + scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members WHERE user_id IS NOT NULL AND requested_at IS NULL)') } scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) } def self.with_two_factor diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb new file mode 100644 index 00000000000..5fdf2bbf7c3 --- /dev/null +++ b/app/serializers/analytics_build_entity.rb @@ -0,0 +1,40 @@ +class AnalyticsBuildEntity < Grape::Entity + include RequestAwareEntity + include EntityDateHelper + + expose :name + expose :id + expose :ref, as: :branch + expose :short_sha + expose :author, using: UserEntity + + expose :started_at, as: :date do |build| + interval_in_words(build[:started_at]) + end + + expose :duration, as: :total_time do |build| + distance_of_time_as_hash(build[:duration].to_f) + end + + expose :branch do + expose :ref, as: :name + + expose :url do |build| + url_to(:namespace_project_tree, build, build.ref) + end + end + + expose :url do |build| + url_to(:namespace_project_build, build) + end + + expose :commit_url do |build| + url_to(:namespace_project_commit, build, build.sha) + end + + private + + def url_to(route, build, id = nil) + public_send("#{route}_url", build.project.namespace, build.project, id || build) + end +end diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb new file mode 100644 index 00000000000..f172d67d356 --- /dev/null +++ b/app/serializers/analytics_build_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsBuildSerializer < BaseSerializer + entity AnalyticsBuildEntity +end diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb new file mode 100644 index 00000000000..402cecbfd08 --- /dev/null +++ b/app/serializers/analytics_commit_entity.rb @@ -0,0 +1,13 @@ +class AnalyticsCommitEntity < CommitEntity + include EntityDateHelper + + expose :short_id, as: :short_sha + + expose :total_time do |commit| + distance_of_time_as_hash(request.total_time.to_f) + end + + unexpose :author_name + unexpose :author_email + unexpose :message +end diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb new file mode 100644 index 00000000000..cdbfecf2b70 --- /dev/null +++ b/app/serializers/analytics_commit_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsCommitSerializer < BaseSerializer + entity AnalyticsCommitEntity +end diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb new file mode 100644 index 00000000000..9f4859e8410 --- /dev/null +++ b/app/serializers/analytics_generic_serializer.rb @@ -0,0 +1,7 @@ +class AnalyticsGenericSerializer < BaseSerializer + def represent(resource, opts = {}) + resource.symbolize_keys! + + super(resource, opts) + end +end diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb new file mode 100644 index 00000000000..44c50f18613 --- /dev/null +++ b/app/serializers/analytics_issue_entity.rb @@ -0,0 +1,29 @@ +class AnalyticsIssueEntity < Grape::Entity + include RequestAwareEntity + include EntityDateHelper + + expose :title + expose :author, using: UserEntity + + expose :iid do |object| + object[:iid].to_s + end + + expose :total_time do |object| + distance_of_time_as_hash(object[:total_time].to_f) + end + + expose(:created_at) do |object| + interval_in_words(object[:created_at]) + end + + expose :url do |object| + url_to(:namespace_project_issue, id: object[:iid].to_s) + end + + private + + def url_to(route, id) + public_send("#{route}_url", request.project.namespace, request.project, id) + end +end diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb new file mode 100644 index 00000000000..4fb3e8f1bb4 --- /dev/null +++ b/app/serializers/analytics_issue_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsIssueSerializer < AnalyticsGenericSerializer + entity AnalyticsIssueEntity +end diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb new file mode 100644 index 00000000000..888265eaa38 --- /dev/null +++ b/app/serializers/analytics_merge_request_entity.rb @@ -0,0 +1,7 @@ +class AnalyticsMergeRequestEntity < AnalyticsIssueEntity + expose :state + + expose :url do |object| + url_to(:namespace_project_merge_request, id: object[:iid].to_s) + end +end diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb new file mode 100644 index 00000000000..4622a1dd855 --- /dev/null +++ b/app/serializers/analytics_merge_request_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer + entity AnalyticsMergeRequestEntity +end diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb new file mode 100644 index 00000000000..b333b3344c3 --- /dev/null +++ b/app/serializers/entity_date_helper.rb @@ -0,0 +1,35 @@ +module EntityDateHelper + include ActionView::Helpers::DateHelper + + def interval_in_words(diff) + "#{distance_of_time_in_words(diff.to_f)} ago" + end + + # Converts seconds into a hash such as: + # { days: 1, hours: 3, mins: 42, seconds: 40 } + # + # It returns 0 seconds for zero or negative numbers + # It rounds to nearest time unit and does not return zero + # i.e { min: 1 } instead of { mins: 1, seconds: 0 } + def distance_of_time_as_hash(diff) + diff = diff.abs.floor + + return { seconds: 0 } if diff == 0 + + mins = (diff / 60).floor + seconds = diff % 60 + hours = (mins / 60).floor + mins = mins % 60 + days = (hours / 24).floor + hours = hours % 24 + + duration_hash = {} + + duration_hash[:days] = days if days > 0 + duration_hash[:hours] = hours if hours > 0 + duration_hash[:mins] = mins if mins > 0 + duration_hash[:seconds] = seconds if seconds > 0 + + duration_hash + end +end diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb new file mode 100644 index 00000000000..2be4d3e6ab5 --- /dev/null +++ b/app/services/after_branch_delete_service.rb @@ -0,0 +1,23 @@ +require_relative 'base_service' + +## +# Branch can be deleted either by DeleteBranchService +# or by GitPushService. +# +class AfterBranchDeleteService < BaseService + attr_reader :branch_name + + def execute(branch_name) + @branch_name = branch_name + + stop_environments + end + + private + + def stop_environments + Ci::StopEnvironmentsService + .new(project, current_user) + .execute(branch_name) + end +end diff --git a/app/services/chat_names/authorize_user_service.rb b/app/services/chat_names/authorize_user_service.rb new file mode 100644 index 00000000000..321bf3a9205 --- /dev/null +++ b/app/services/chat_names/authorize_user_service.rb @@ -0,0 +1,38 @@ +module ChatNames + class AuthorizeUserService + include Gitlab::Routing.url_helpers + + def initialize(service, params) + @service = service + @params = params + end + + def execute + return unless chat_name_params.values.all?(&:present?) + + token = request_token + + new_profile_chat_name_url(token: token) if token + end + + private + + def request_token + chat_name_token.store!(chat_name_params) + end + + def chat_name_token + Gitlab::ChatNameToken.new + end + + def chat_name_params + { + service_id: @service.id, + team_id: @params[:team_id], + team_domain: @params[:team_domain], + chat_id: @params[:user_id], + chat_name: @params[:user_name] + } + end + end +end diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb new file mode 100644 index 00000000000..4f5c5567b42 --- /dev/null +++ b/app/services/chat_names/find_user_service.rb @@ -0,0 +1,26 @@ +module ChatNames + class FindUserService + def initialize(service, params) + @service = service + @params = params + end + + def execute + chat_name = find_chat_name + return unless chat_name + + chat_name.touch(:last_used_at) + chat_name.user + end + + private + + def find_chat_name + ChatName.find_by( + service: @service, + team_id: @params[:team_id], + chat_id: @params[:user_id] + ) + end + end +end diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb new file mode 100644 index 00000000000..cf590459cb2 --- /dev/null +++ b/app/services/ci/stop_environments_service.rb @@ -0,0 +1,29 @@ +module Ci + class StopEnvironmentsService < BaseService + attr_reader :ref + + def execute(branch_name) + @ref = branch_name + + return unless has_ref? + + environments.each do |environment| + next unless environment.stoppable? + next unless can?(current_user, :create_deployment, project) + + environment.stop!(current_user) + end + end + + private + + def has_ref? + @ref.present? + end + + def environments + @environments ||= project + .environments_recently_updated_on_branch(@ref) + end + end +end diff --git a/app/services/destroy_group_service.rb b/app/services/destroy_group_service.rb index 0081364b8aa..a880952e274 100644 --- a/app/services/destroy_group_service.rb +++ b/app/services/destroy_group_service.rb @@ -6,12 +6,10 @@ class DestroyGroupService end def async_execute - group.transaction do - # Soft delete via paranoia gem - group.destroy - job_id = GroupDestroyWorker.perform_async(group.id, current_user.id) - Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}") - end + # Soft delete via paranoia gem + group.destroy + job_id = GroupDestroyWorker.perform_async(group.id, current_user.id) + Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}") end def execute diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index de313095bed..77c6c81cc1b 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -49,10 +49,7 @@ class GitPushService < BaseService update_gitattributes if is_default_branch? end - # Update merge requests that may be affected by this push. A new branch - # could cause the last commit of a merge request to change. - update_merge_requests - + execute_related_hooks perform_housekeeping end @@ -62,14 +59,24 @@ class GitPushService < BaseService protected - def update_merge_requests - UpdateMergeRequestsWorker.perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref]) + def execute_related_hooks + # Update merge requests that may be affected by this push. A new branch + # could cause the last commit of a merge request to change. + # + UpdateMergeRequestsWorker + .perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref]) EventCreateService.new.push(@project, current_user, build_push_data) @project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks) Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute ProjectCacheWorker.perform_async(@project.id) + + if push_remove_branch? + AfterBranchDeleteService + .new(project, current_user) + .execute(branch_name) + end end def perform_housekeeping diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index bb92cd80cc9..575795788de 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -212,9 +212,9 @@ class IssuableBaseService < BaseService def change_subscription(issuable) case params.delete(:subscription_event) when 'subscribe' - issuable.subscribe(current_user) + issuable.subscribe(current_user, project) when 'unsubscribe' - issuable.unsubscribe(current_user) + issuable.unsubscribe(current_user, project) end end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 22596b4014a..4a7e6930842 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -60,15 +60,7 @@ module MergeRequests merge_requests = filter_merge_requests(merge_requests) merge_requests.each do |merge_request| - if merge_request.source_branch == @branch_name || force_push? - merge_request.reload_diff - else - mr_commit_ids = merge_request.commits.map(&:id) - push_commit_ids = @commits.map(&:id) - matches = mr_commit_ids & push_commit_ids - merge_request.reload_diff if matches.any? - end - + reload_diff(merge_request) unless branch_removed? merge_request.mark_as_unchecked end end @@ -173,5 +165,16 @@ module MergeRequests def branch_removed? Gitlab::Git.blank_ref?(@newrev) end + + def reload_diff(merge_request) + if merge_request.source_branch == @branch_name || force_push? + merge_request.reload_diff + else + mr_commit_ids = merge_request.commits.map(&:id) + push_commit_ids = @commits.map(&:id) + matches = mr_commit_ids & push_commit_ids + merge_request.reload_diff if matches.any? + end + end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 6697840cc26..ecdcbf08ee1 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -75,7 +75,7 @@ class NotificationService # * watchers of the issue's labels # def relabeled_issue(issue, added_labels, current_user) - relabeled_resource_email(issue, added_labels, current_user, :relabeled_issue_email) + relabeled_resource_email(issue, issue.project, added_labels, current_user, :relabeled_issue_email) end # When create a merge request we should send an email to: @@ -118,7 +118,7 @@ class NotificationService # * watchers of the mr's labels # def relabeled_merge_request(merge_request, added_labels, current_user) - relabeled_resource_email(merge_request, added_labels, current_user, :relabeled_merge_request_email) + relabeled_resource_email(merge_request, merge_request.target_project, added_labels, current_user, :relabeled_merge_request_email) end def close_mr(merge_request, current_user) @@ -205,7 +205,7 @@ class NotificationService recipients = reject_muted_users(recipients, note.project) - recipients = add_subscribed_users(recipients, note.noteable) + recipients = add_subscribed_users(recipients, note.project, note.noteable) recipients = reject_unsubscribed_users(recipients, note.noteable) recipients = reject_users_without_access(recipients, note.noteable) @@ -393,7 +393,7 @@ class NotificationService ) end - # Build a list of users based on project notifcation settings + # Build a list of users based on project notification settings def select_project_member_setting(project, global_setting, users_global_level_watch) users = notification_settings_for(project, :watch) @@ -505,17 +505,17 @@ class NotificationService end end - def add_subscribed_users(recipients, target) + def add_subscribed_users(recipients, project, target) return recipients unless target.respond_to? :subscribers - recipients + target.subscribers + recipients + target.subscribers(project) end - def add_labels_subscribers(recipients, target, labels: nil) + def add_labels_subscribers(recipients, project, target, labels: nil) return recipients unless target.respond_to? :labels (labels || target.labels).each do |label| - recipients += label.subscribers + recipients += label.subscribers(project) end recipients @@ -571,8 +571,8 @@ class NotificationService end end - def relabeled_resource_email(target, labels, current_user, method) - recipients = build_relabeled_recipients(target, current_user, labels: labels) + def relabeled_resource_email(target, project, labels, current_user, method) + recipients = build_relabeled_recipients(target, project, current_user, labels: labels) label_names = labels.map(&:name) recipients.each do |recipient| @@ -608,10 +608,10 @@ class NotificationService end recipients = reject_muted_users(recipients, project) - recipients = add_subscribed_users(recipients, target) + recipients = add_subscribed_users(recipients, project, target) if [:new_issue, :new_merge_request].include?(custom_action) - recipients = add_labels_subscribers(recipients, target) + recipients = add_labels_subscribers(recipients, project, target) end recipients = reject_unsubscribed_users(recipients, target) @@ -622,8 +622,8 @@ class NotificationService recipients.uniq end - def build_relabeled_recipients(target, current_user, labels:) - recipients = add_labels_subscribers([], target, labels: labels) + def build_relabeled_recipients(target, project, current_user, labels:) + recipients = add_labels_subscribers([], project, target, labels: labels) recipients = reject_unsubscribed_users(recipients, target) recipients = reject_users_without_access(recipients, target) recipients.delete(current_user) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 15d7918e7fd..28db145a1f4 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -95,7 +95,7 @@ module Projects unless @project.gitlab_project_import? @project.create_wiki unless skip_wiki? - @project.build_missing_services + create_services_from_active_templates(@project) @project.create_labels end @@ -135,5 +135,12 @@ module Projects @project end + + def create_services_from_active_templates(project) + Service.where(template: true, active: true).each do |template| + service = Service.build_from_template(project.id, template) + service.save! + end + end end end diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb index 5a81194a5f4..d75c5b1800e 100644 --- a/app/services/slash_commands/interpret_service.rb +++ b/app/services/slash_commands/interpret_service.rb @@ -193,7 +193,7 @@ module SlashCommands desc 'Subscribe' condition do issuable.persisted? && - !issuable.subscribed?(current_user) + !issuable.subscribed?(current_user, project) end command :subscribe do @updates[:subscription_event] = 'subscribe' @@ -202,7 +202,7 @@ module SlashCommands desc 'Unsubscribe' condition do issuable.persisted? && - issuable.subscribed?(current_user) + issuable.subscribed?(current_user, project) end command :unsubscribe do @updates[:subscription_event] = 'unsubscribe' diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml index 26a8846b609..5e3f105d41f 100644 --- a/app/views/admin/builds/index.html.haml +++ b/app/views/admin/builds/index.html.haml @@ -14,5 +14,5 @@ .row-content-block.second-block #{(@scope || 'all').capitalize} builds - %ul.content-list.builds-content-list + %ul.content-list.builds-content-list.admin-builds-table = render "projects/builds/table", builds: @builds, admin: true diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 817910f7ddf..589f4557b52 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -7,7 +7,7 @@ .col-sm-10 = render 'shared/choose_group_avatar_button', f: f - = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group + = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group .form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml index eb09a6328ed..c2b9807015d 100644 --- a/app/views/admin/groups/edit.html.haml +++ b/app/views/admin/groups/edit.html.haml @@ -1,4 +1,4 @@ - page_title "Edit", @group.name, "Groups" %h3.page-title Edit group: #{@group.name} %hr -= render 'form' += render 'form', visibility_level: @group.visibility_level diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml index c81ee552ac3..8f9fe96249f 100644 --- a/app/views/admin/groups/new.html.haml +++ b/app/views/admin/groups/new.html.haml @@ -1,4 +1,4 @@ - page_title "New Group" %h3.page-title New group %hr -= render 'form' += render 'form', visibility_level: default_group_visibility diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 7c68e3266e5..3133f6de2e8 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -8,7 +8,7 @@ = f.text_field :name, class: "form-control top", required: true, title: "This field is required." %div.username.form-group = f.label :username - = f.text_field :username, class: "form-control middle", pattern: "[a-zA-Z0-9]+", required: true, title: 'Please create a username with only alphanumeric characters.' + = f.text_field :username, class: "form-control middle", pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE, required: true, title: 'Please create a username with only alphanumeric characters.' %p.validation-error.hide Username is already taken. %p.validation-success.hide Username is available. %p.validation-pending.hide Checking username availability... diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml index 077e8e64e5f..e4b4ea675d2 100644 --- a/app/views/discussions/_discussion.html.haml +++ b/app/views/discussions/_discussion.html.haml @@ -1,9 +1,6 @@ - expanded = discussion.expanded? %li.note.note-discussion.timeline-entry .timeline-entry-inner - .timeline-icon - = link_to user_path(discussion.author) do - = image_tag avatar_icon(discussion.author), class: "avatar s40" .timeline-content .discussion.js-toggle-container{ class: discussion.id, data: { discussion_id: discussion.id } } .discussion-header @@ -13,9 +10,7 @@ = icon("chevron-up") - else = icon("chevron-down") - Toggle discussion - = link_to_member(@project, discussion.author, avatar: false) .inline.discussion-headline-light @@ -38,8 +33,6 @@ = time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago") - = render "discussions/headline", discussion: discussion - .discussion-body.js-toggle-content{ class: ("hide" unless expanded) } - if discussion.diff_discussion? && discussion.diff_file = render "discussions/diff_with_notes", discussion: discussion diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 8aefdcb3d9b..a9a0b149049 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -26,5 +26,5 @@ = render "layouts/flash" = yield :flash_message %div{ class: "#{(container_class unless @no_container)} #{@content_class}" } - .content + .content{ id: "content-body" } = yield diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 7a9859262f7..5456be77aab 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -1,4 +1,5 @@ %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } + %a{ href: "#content-body", tabindex: "1", class: "sr-only gl-accessibility" } Skip to content %div{ class: "container-fluid" } .header-content %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" } diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index a0356feef95..2a6d9cda379 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -26,12 +26,12 @@ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do %span Issues - %span.count= number_with_delimiter(current_user.assigned_issues.opened.count) + %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened)) = nav_link(path: 'dashboard#merge_requests') do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do %span Merge Requests - %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) + %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened)) = nav_link(controller: 'dashboard/snippets') do = link_to dashboard_snippets_path, title: 'Snippets' do %span diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 6d514f669db..e06301bda14 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -17,6 +17,10 @@ = link_to applications_profile_path, title: 'Applications' do %span Applications + = nav_link(controller: :chat_names) do + = link_to profile_chat_names_path, title: 'Chat' do + %span + Chat = nav_link(controller: :personal_access_tokens) do = link_to profile_personal_access_tokens_path, title: 'Access Tokens' do %span diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index c0c07d65daa..307c5a11206 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -27,9 +27,9 @@ %h4 #{pluralize @message.diffs_count, "changed file"}: %ul - - @message.diffs.each_with_index do |diff, i| + - @message.diffs.each do |diff| %li.file-stats - %a{href: "#{@message.target_url if @message.disable_diffs?}#diff-#{i}" } + %a{href: "#{@message.target_url if @message.disable_diffs?}##{hexdigest(diff.file_path)}" } - if diff.deleted_file %span.deleted-file − @@ -52,9 +52,10 @@ %h5 The diff was not included because it is too large. - else %h4 Changes: - - diff_files.each_with_index do |diff_file, i| - %li{id: "diff-#{i}"} - %a{href: @message.target_url + "#diff-#{i}"}< + - diff_files.each do |diff_file| + - file_hash = hexdigest(diff_file.file_path) + %li{id: file_hash} + %a{href: @message.target_url + "##{file_hash}"}< - if diff_file.deleted_file %strong< = diff_file.old_path diff --git a/app/views/profiles/chat_names/_chat_name.html.haml b/app/views/profiles/chat_names/_chat_name.html.haml new file mode 100644 index 00000000000..6b32d377e1a --- /dev/null +++ b/app/views/profiles/chat_names/_chat_name.html.haml @@ -0,0 +1,27 @@ +- service = chat_name.service +- project = service.project +%tr + %td + %strong + - if can?(current_user, :read_project, project) + = link_to project.name_with_namespace, project_path(project) + - else + .light N/A + %td + %strong + - if can?(current_user, :admin_project, project) + = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) + - else + = service.title + %td + = chat_name.team_domain + %td + = chat_name.chat_name + %td + - if chat_name.last_used_at + time_ago_with_tooltip(chat_name.last_used_at) + - else + Never + + %td + = link_to 'Remove', profile_chat_name_path(chat_name), method: :delete, class: 'btn btn-danger pull-right', data: { confirm: 'Are you sure you want to revoke this nickname?' } diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml new file mode 100644 index 00000000000..20cc636b2da --- /dev/null +++ b/app/views/profiles/chat_names/index.html.haml @@ -0,0 +1,30 @@ +- page_title 'Chat' += render 'profiles/head' + +.row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + = page_title + %p + You can see your Chat accounts. + + .col-lg-9 + %h5 Active chat names (#{@chat_names.size}) + + - if @chat_names.present? + .table-responsive + %table.table.chat-names + %thead + %tr + %th Project + %th Service + %th Team domain + %th Nickname + %th Last used + %th + %tbody + = render @chat_names + + - else + .settings-message.text-center + You don't have any active chat names. diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml new file mode 100644 index 00000000000..f635acf96e2 --- /dev/null +++ b/app/views/profiles/chat_names/new.html.haml @@ -0,0 +1,15 @@ +%h3.page-title Authorization required +%main{:role => "main"} + %p.h4 + Authorize + %strong.text-info= @chat_name_params[:chat_name] + to use your account? + + %hr + .actions + = form_tag profile_chat_names_path, method: :post do + = hidden_field_tag :token, @chat_name_token.token + = submit_tag "Authorize", class: "btn btn-success wide pull-left" + = form_tag deny_profile_chat_names_path, method: :delete do + = hidden_field_tag :token, @chat_name_token.token + = submit_tag "Deny", class: "btn btn-danger prepend-left-10" diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml index 47165c70097..a2e5118a9f3 100644 --- a/app/views/projects/boards/components/_board.html.haml +++ b/app/views/projects/boards/components/_board.html.haml @@ -7,7 +7,7 @@ data: { container: "body", placement: "bottom" } } {{ list.title }} .board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' } - %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" }' } + %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" && !disabled }' } {{ list.issuesSize }} - if can?(current_user, :admin_issue, @project) %button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button", diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml index 9f69bd64f71..f6aa20c4579 100644 --- a/app/views/projects/builds/_header.html.haml +++ b/app/views/projects/builds/_header.html.haml @@ -17,6 +17,6 @@ = render "user" = time_ago_with_tooltip(@build.created_at) - if can?(current_user, :update_build, @build) && @build.retryable? - = link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted pull-right', method: :post + = link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post %button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" } = icon('angle-double-left') diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index f533eec642e..d8cbfd7173a 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -26,6 +26,30 @@ = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do Runners page + - if @build.starts_environment? + .prepend-top-default + .environment-information + - if @build.outdated_deployment? + = ci_icon_for_status('success_with_warnings') + - else + = ci_icon_for_status(@build.status) + + - environment = environment_for_build(@build.project, @build) + - if @build.success? && @build.last_deployment.present? + - if @build.last_deployment.last? + This build is the most recent deployment to #{environment_link_for_build(@build.project, @build)}. + - else + This build is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}. + - if environment.last_deployment + View the most recent deployment #{deployment_link(environment.last_deployment)}. + - elsif @build.complete? && !@build.success? + The deployment of this build to #{environment_link_for_build(@build.project, @build)} did not succeed. + - else + This build is creating a deployment to #{environment_link_for_build(@build.project, @build)} + - if environment.last_deployment + and will overwrite the + = link_to 'latest deployment', deployment_link(environment.last_deployment) + .prepend-top-default - if @build.erased? .erased.alert.alert-warning diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 0ebc38d16cf..503cbd13b5e 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,5 +1,5 @@ -.commit-info-row.commit-info-row-header - .commit-meta +.page-content-header + .header-main-content %strong Commit %strong.monospace.js-details-short= @commit.short_id = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do @@ -19,7 +19,8 @@ %strong = commit_committer_link(@commit, avatar: true, size: 24) #{time_ago_with_tooltip(@commit.committed_date)} - .commit-action-buttons + + .header-action-buttons - if defined?(@notes_count) && @notes_count > 0 %span.btn.disabled.btn-grouped.hidden-xs.append-right-10 = icon('comment') @@ -55,8 +56,8 @@ %pre.commit-description = preserve(markdown(@commit.description, pipeline: :single_line, author: @commit.author)) -.commit-info-widget - .widget-row.branch-info +.info-well + .well-segment.branch-info .icon-container.commit-icon = custom_icon("icon_commit") %span.cgray= pluralize(@commit.parents.count, "parent") @@ -66,7 +67,7 @@ %i.fa.fa-spinner.fa-spin - if @commit.status - .widget-row.pipeline-info + .well-segment.pipeline-info .icon-container = ci_icon_for_status(@commit.status) Pipeline diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index 062a8905a19..1174158eb65 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -1,10 +1,6 @@ .pipeline-graph-container .row-content-block.build-content.middle-block.pipeline-actions .pull-right - %button.btn.btn-grouped.btn-white.toggle-pipeline-btn - %span.toggle-btn-text Hide - %span pipeline graph - %span.caret - if can?(current_user, :update_pipeline, pipeline.project) - if pipeline.builds.latest.failed.any?(&:retryable?) = link_to "Retry failed", retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-grouped btn-primary', method: :post diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 34855c54176..12096941209 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -36,7 +36,6 @@ %pre.commit-row-description.js-toggle-content = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) - .commit-row-info - = commit_author_link(commit, avatar: false, size: 24) - authored - #{time_ago_with_tooltip(commit.committed_date)} + = commit_author_link(commit, avatar: false, size: 24) + authored + #{time_ago_with_tooltip(commit.committed_date)} diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 067cf595da3..ab4a2dc36e5 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -22,11 +22,12 @@ = render 'projects/diffs/warning', diff_files: diff_files .files{ data: { can_create_note: can_create_note } } - - diff_files.each_with_index do |diff_file, index| + - diff_files.each_with_index do |diff_file| - diff_commit = commit_for_diff(diff_file) - blob = diff_file.blob(diff_commit) - next unless blob - blob.load_all_data!(diffs.project.repository) unless blob.only_display_raw? + - file_hash = hexdigest(diff_file.file_path) - = render 'projects/diffs/file', index: index, project: diffs.project, + = render 'projects/diffs/file', file_hash: file_hash, project: diffs.project, diff_file: diff_file, diff_commit: diff_commit, blob: blob diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 8f4f9ad4a80..120ba9ffcd2 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,6 +1,6 @@ -.diff-file.file-holder{id: "diff-#{index}", data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)} +.diff-file.file-holder{id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)} .file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"} - = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "#diff-#{index}" + = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" - unless diff_file.submodule? .file-actions.hidden-xs diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index e751dabdf99..66d6254aa1e 100644 --- a/app/views/projects/diffs/_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml @@ -9,28 +9,29 @@ %strong.cred #{diff_files.sum(&:removed_lines)} deletions .file-stats.js-toggle-content.hide %ul - - diff_files.each_with_index do |diff_file, i| + - diff_files.each do |diff_file| + - file_hash = hexdigest(diff_file.file_path) %li - if diff_file.deleted_file %span.deleted-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-minus = diff_file.old_path - elsif diff_file.renamed_file %span.renamed-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-minus = diff_file.old_path → = diff_file.new_path - elsif diff_file.new_file %span.new-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-plus = diff_file.new_path - else %span.edit-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-adjust = diff_file.new_path diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index 31d3ec23276..747bfa554cb 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -19,11 +19,17 @@ in - project = merge_request.target_project = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) - %span.merge-request-status.prepend-left-10 - - if merge_request.merged? - MERGED - - elsif merge_request.closed? - CLOSED + + - if merge_request.merged? + %span.merge-request-status.prepend-left-10.merged + Merged + - elsif merge_request.closed? + %span.merge-request-status.prepend-left-10.closed + Closed + - else + %span.merge-request-status.prepend-left-10.open + Open + - if @closed_by_merge_requests.present? %li = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 12408068834..9ffcc48eb80 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -54,15 +54,18 @@ = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do = icon('code-fork') = merge_request.target_branch + - if merge_request.milestone = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do = icon('clock-o') = merge_request.milestone.title + - if merge_request.labels.any? - merge_request.labels.each do |label| = link_to_label(label, subject: merge_request.project, type: :merge_request) + - if merge_request.tasks? %span.task-status diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index afff15228c1..89ae64554c0 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -14,6 +14,9 @@ = note.author.to_reference - unless note.system commented + - if note.system + %span{class: 'system-note-message'} + = h(note.note_html.downcase.html_safe) %a{ href: "##{dom_id(note)}" } = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago') - unless note.system? @@ -67,7 +70,9 @@ = render 'projects/notes/edit_form', note: note .note-awards = render 'award_emoji/awards_block', awardable: note, inline: false - + - if note.system + .system-note-commit-list-toggler + Toggle commit list - if note.attachment.url .note-attachment - if note.attachment.image? diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index d288efc546f..a0de125d765 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,39 +1,49 @@ -%p -.commit-info-row - Pipeline - = link_to "##{@pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, @pipeline.id), class: "monospace" - with - = pluralize @pipeline.statuses.count(:id), "build" - - if @pipeline.ref - for - = link_to @pipeline.ref, namespace_project_commits_path(@project.namespace, @project, @pipeline.ref), class: "monospace" - - if @pipeline.duration - in - = time_interval_in_words(@pipeline.duration) - - if @pipeline.queued_duration - = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})" - - .pull-right +.page-content-header + .header-main-content = link_to namespace_project_pipeline_path(@project.namespace, @project, @pipeline), class: "ci-status ci-#{@pipeline.status}" do = ci_icon_for_status(@pipeline.status) = ci_label_for_status(@pipeline.status) + %strong Pipeline ##{@commit.pipelines.last.id} + triggered #{time_ago_with_tooltip(@commit.authored_date)} by + = author_avatar(@commit, size: 24) + = commit_author_link(@commit) + .header-action-buttons + - if can?(current_user, :update_pipeline, @pipeline.project) + - if @pipeline.builds.latest.failed.any?(&:retryable?) + = link_to "Retry failed", retry_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'btn btn-inverted-secondary', method: :post + - if @pipeline.builds.running_or_pending.any? + = link_to "Cancel running", cancel_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post - if @commit - .commit-info-row - %span.light Authored by - %strong - = commit_author_link(@commit, avatar: true, size: 24) - #{time_ago_with_tooltip(@commit.authored_date)} - -.commit-info-row - %span.light Commit - = link_to @pipeline.sha, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "monospace" - = clipboard_button(clipboard_text: @pipeline.sha) - -- if @commit - .commit-box.content-block + .commit-box %h3.commit-title = markdown(@commit.title, pipeline: :single_line) - if @commit.description.present? %pre.commit-description = preserve(markdown(@commit.description, pipeline: :single_line)) + +.info-well + - if @commit.status + .well-segment.pipeline-info + .icon-container + = ci_icon_for_status(@commit.status) + = pluralize @pipeline.statuses.count(:id), "build" + - if @pipeline.ref + from + = link_to @pipeline.ref, namespace_project_commits_path(@project.namespace, @project, @pipeline.ref), class: "monospace" + - if @pipeline.duration + in + = time_interval_in_words(@pipeline.duration) + - if @pipeline.queued_duration + = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})" + + .well-segment.branch-info + .icon-container.commit-icon + = custom_icon("icon_commit") + = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "monospace js-details-short" + = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do + %span.text-expander + \... + %span.js-details-content.hide + = link_to @pipeline.sha, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "monospace commit-hash-full" + = clipboard_button(clipboard_text: @pipeline.sha, title: "Copy commit SHA to clipboard") diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml new file mode 100644 index 00000000000..718314701f9 --- /dev/null +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -0,0 +1,51 @@ +.tabs-holder + %ul.nav-links.no-top.no-bottom + %li.active + = link_to "Pipeline", "#js-tab-pipeline", data: { target: '#js-tab-pipeline', action: 'pipeline', toggle: 'tab' }, class: 'pipeline-tab' + %li + = link_to "#js-tab-builds", data: { target: '#js-tab-builds', action: 'build', toggle: 'tab' }, class: 'builds-tab' do + Builds + %span.badge= pipeline.statuses.count + +.tab-content + #js-tab-pipeline.tab-pane.active + .build-content.middle-block.pipeline-graph + .pipeline-visualization + %ul.stage-column-list + - stages = pipeline.stages_with_latest_statuses + - stages.each do |stage, statuses| + %li.stage-column + .stage-name + %a{name: stage} + - if stage + = stage.titleize + .builds-container + %ul + = render "projects/commit/pipeline_stage", statuses: statuses + + #js-tab-builds.tab-pane + - if pipeline.yaml_errors.present? + .bs-callout.bs-callout-danger + %h4 Found errors in your .gitlab-ci.yml: + %ul + - pipeline.yaml_errors.split(",").each do |error| + %li= error + You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} + + - if pipeline.project.builds_enabled? && !pipeline.ci_yaml_file + .bs-callout.bs-callout-warning + \.gitlab-ci.yml not found in this commit + + .table-holder.pipeline-holder + %table.table.ci-table.pipeline + %thead + %tr + %th Status + %th Build ID + %th Name + %th + - if pipeline.project.build_coverage_enabled? + %th Coverage + %th + - pipeline.statuses.relevant.stages.each do |stage| + = render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.relevant.where(stage: stage) diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 688535ad764..8c6652a5f90 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -3,9 +3,7 @@ = render "projects/pipelines/head" %div{ class: container_class } - .prepend-top-default - - if @commit - = render "projects/pipelines/info" - %div.block-connector + - if @commit + = render "projects/pipelines/info" - = render "projects/commit/pipeline", pipeline: @pipeline + = render "projects/pipelines/with_tabs", pipeline: @pipeline diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index 4a33a5bc6f6..66fd3029dc9 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -28,5 +28,6 @@ %td.hidden-xs = service.description %td.light - = time_ago_in_words service.updated_at - ago + - if service.updated_at.present? + = time_ago_in_words service.updated_at + ago diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 6f0a0ea36ec..9e8adc82583 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -1,11 +1,13 @@ -- blob = parse_search_result(blob) +- file_name, blob = blob .blob-result .file-holder .file-title - - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename)) + - ref = @search_results.repository_ref + - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(ref, file_name)) = link_to blob_link do %i.fa.fa-file %strong - = blob.filename - .file-content.code.term - = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link + = file_name + - if blob + .file-content.code.term + = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 6ccdef0df46..db324d8868e 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -1,6 +1,7 @@ - label_css_id = dom_id(label) - open_issues_count = label.open_issues_count(current_user) - open_merge_requests_count = label.open_merge_requests_count(current_user) +- status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] %li{id: label_css_id, data: { id: label.id } } @@ -18,10 +19,19 @@ %li = link_to_label(label, subject: subject) do = pluralize open_issues_count, 'open issue' - - if current_user - %li.label-subscription{ data: toggle_subscription_data(label) } - %a.js-subscribe-button.label-subscribe-button.subscription-status{ role: "button", href: "#", data: { toggle: "tooltip", status: label_subscription_status(label) } } - %span= label_subscription_toggle_button_text(label) + - if current_user && defined?(@project) + %li.label-subscription + - if label.is_a?(ProjectLabel) + %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', data: { status: status, url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } + %span= label_subscription_toggle_button_text(label, @project) + - else + %a.js-unsubscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' if status.unsubscribed?), data: { url: group_label_unsubscribe_path(label, @project) } } + %span Unsubscribe + %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } + %span Subscribe at project level + %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } + %span Subscribe at group level + - if can?(current_user, :admin_label, label) %li = link_to 'Edit', edit_label_path(label) @@ -34,12 +44,27 @@ = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do = pluralize open_issues_count, 'open issue' - - if current_user - .label-subscription.inline{ data: toggle_subscription_data(label) } - %button.js-subscribe-button.label-subscribe-button.btn.btn-transparent.btn-action.subscription-status{ type: "button", title: label_subscription_toggle_button_text(label), data: { toggle: "tooltip", status: label_subscription_status(label) } } - %span.sr-only= label_subscription_toggle_button_text(label) - = icon('eye', class: 'label-subscribe-button-icon', disabled: label.is_a?(GroupLabel)) - = icon('spinner spin', class: 'label-subscribe-button-loading') + - if current_user && defined?(@project) + .label-subscription.inline + - if label.is_a?(ProjectLabel) + %button.js-subscribe-button.label-subscribe-button.btn.btn-default.btn-action{ type: 'button', title: label_subscription_toggle_button_text(label, @project), data: { toggle: 'tooltip', status: status, url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } + %span= label_subscription_toggle_button_text(label, @project) + = icon('spinner spin', class: 'label-subscribe-button-loading') + - else + %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default.btn-action{ type: 'button', class: ('hidden' if status.unsubscribed?), title: 'Unsubscribe', data: { toggle: 'tooltip', url: group_label_unsubscribe_path(label, @project) } } + %span Unsubscribe + = icon('spinner spin', class: 'label-subscribe-button-loading') + + .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } + %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %span Subscribe + = icon('chevron-down') + %ul.dropdown-menu + %li + %a.js-subscribe-button{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } + Project level + %a.js-subscribe-button{ data: { url: toggle_subscription_group_label_path(label.group, label) } } + Group level - if can?(current_user, :admin_label, label) = link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do @@ -49,6 +74,10 @@ %span.sr-only Delete = icon('trash-o') - - if current_user && label.is_a?(ProjectLabel) - :javascript - new Subscription('##{dom_id(label)} .label-subscription'); + - if current_user && defined?(@project) + - if label.is_a?(ProjectLabel) + :javascript + new gl.ProjectLabelSubscription('##{dom_id(label)} .label-subscription'); + - else + :javascript + new gl.GroupLabelSubscription('##{dom_id(label)} .label-subscription'); diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index 5254d265918..601ef51737a 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -10,26 +10,27 @@ .col-sm-10 = form.check_box :active -.form-group - = form.label :url, "Trigger", class: 'control-label' - - .col-sm-10 - - @service.supported_events.each do |event| - %div - = form.check_box service_event_field_name(event), class: 'pull-left' - .prepend-left-20 - = form.label service_event_field_name(event), class: 'list-label' do - %strong - = event.humanize - - - field = @service.event_field(event) - - - if field - %p - = form.text_field field[:name], class: "form-control", placeholder: field[:placeholder] - - %p.light - = service_event_description(event) +- if @service.supported_events.present? + .form-group + = form.label :url, "Trigger", class: 'control-label' + + .col-sm-10 + - @service.supported_events.each do |event| + %div + = form.check_box service_event_field_name(event), class: 'pull-left' + .prepend-left-20 + = form.label service_event_field_name(event), class: 'list-label' do + %strong + = event.humanize + + - field = @service.event_field(event) + + - if field + %p + = form.text_field field[:name], class: "form-control", placeholder: field[:placeholder] + + %p.light + = service_event_description(event) - @service.global_fields.each do |field| - type = field[:type] diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg index 3420af411f6..e0a2d4282f0 100644 --- a/app/views/shared/icons/_icon_status_skipped.svg +++ b/app/views/shared/icons/_icon_status_skipped.svg @@ -1 +1 @@ -<svg width="14" height="14" class="ci-status-icon-skipped" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Group Copy 31</title><g fill="#5C5C5C" fill-rule="evenodd"><path d="M10 17.857c4.286 0 7.857-3.571 7.857-7.857S14.286 2.143 10 2.143 2.143 5.714 2.143 10 5.714 17.857 10 17.857M10 0c5.571 0 10 4.429 10 10s-4.429 10-10 10S0 15.571 0 10 4.429 0 10 0"/><path d="M10.986 11l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.644-2.644a1.505 1.505 0 0 0 0-2.126l-2.644-2.644a1 1 0 0 0-1.414 1.414L10.986 9H6.4a1 1 0 0 0 0 2h4.586z"/></g></svg> +<svg width="14" height="14" class="ci-status-icon-skipped" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g fill="#5C5C5C" fill-rule="evenodd"><path d="M10 17.857c4.286 0 7.857-3.571 7.857-7.857S14.286 2.143 10 2.143 2.143 5.714 2.143 10 5.714 17.857 10 17.857M10 0c5.571 0 10 4.429 10 10s-4.429 10-10 10S0 15.571 0 10 4.429 0 10 0"/><path d="M10.986 11l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.644-2.644a1.505 1.505 0 0 0 0-2.126l-2.644-2.644a1 1 0 0 0-1.414 1.414L10.986 9H6.4a1 1 0 0 0 0 2h4.586z"/></g></svg> diff --git a/app/views/shared/issuable/_label_page_create.html.haml b/app/views/shared/issuable/_label_page_create.html.haml index 3bc57d3d2ac..bd66f39fa59 100644 --- a/app/views/shared/issuable/_label_page_create.html.haml +++ b/app/views/shared/issuable/_label_page_create.html.haml @@ -9,7 +9,7 @@   .dropdown-label-color-input .dropdown-label-color-preview.js-dropdown-label-color-preview - %input#new_label_color.default-dropdown-input{ type: "text" } + %input#new_label_color.default-dropdown-input{ type: "text", placeholder: "Assign custom color like #FF0000" } .clearfix %button.btn.btn-primary.pull-left.js-new-label-btn{ type: "button" } Create diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 7363ead09ff..f166fac105d 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -140,7 +140,7 @@ = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user - - subscribed = issuable.subscribed?(current_user) + - subscribed = issuable.subscribed?(current_user, @project) .block.light.subscription{data: {url: toggle_subscription_path(issuable)}} .sidebar-collapsed-icon = icon('rss') diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb index e0ad5268664..e17add7421f 100644 --- a/app/workers/build_success_worker.rb +++ b/app/workers/build_success_worker.rb @@ -4,15 +4,13 @@ class BuildSuccessWorker def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| - create_deployment(build) + create_deployment(build) if build.has_environment? end end private def create_deployment(build) - return if build.environment.blank? - service = CreateDeploymentService.new( build.project, build.user, environment: build.environment, diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb index 34f6ef161fb..070943f1ecc 100644 --- a/app/workers/pipeline_metrics_worker.rb +++ b/app/workers/pipeline_metrics_worker.rb @@ -12,11 +12,11 @@ class PipelineMetricsWorker private def update_metrics_for_active_pipeline(pipeline) - metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil) + metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil, pipeline_id: pipeline.id) end def update_metrics_for_succeeded_pipeline(pipeline) - metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at) + metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at, pipeline_id: pipeline.id) end def metrics(pipeline) |