diff options
author | Lin Jen-Shin <godfat@godfat.org> | 2016-11-04 22:37:44 +0800 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2016-11-04 22:37:44 +0800 |
commit | fb9a0e5277f9825d7faf991c0600a7247b268fe8 (patch) | |
tree | 35000cd71ebf5de840c2c931d2f1c7a9b7357dad /app | |
parent | ce1dc4c25d3464b7a9a1b21d93157c9fed98f705 (diff) | |
parent | 5368b9f249485e254a90fe7daa551d61412bee26 (diff) | |
download | gitlab-ce-fb9a0e5277f9825d7faf991c0600a7247b268fe8.tar.gz |
Merge remote-tracking branch 'upstream/master' into show-status-from-branch
* upstream/master: (35 commits)
Only skip group when it's actually a group in the "Share with group" select
Fix: Todos Filter Shows All Users
Fix: Guest sees some repository details and gets 404
Move shared params to a helper
GrapeDSL for project hooks
Update commits.scss
updated styling commit SHA on branches page + added to changelog
change build list height to show 6,5 builds + improve padding of list, with first/last child selectors
Ignore builds directory from eslint
Add changelog entry
Document multiple repository storage paths
Allow multiple repository storage shards to be enabled, and automatically round-robin between them
Cleaned up global namespace JS
Add tip for using Chrome to run and debug teaspoon tests.
Add CHANGELOG entry file
Add jquery.timeago.js to application.js
Update match-regex to fix filename convention
Move jquery.timeago to vendor directory
Change a bunch of doc links to either relative or https://docs.gitlab.com.
Show log corresponding to env in admin/logs
...
Diffstat (limited to 'app')
53 files changed, 304 insertions, 386 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 7ebe1599fca..1cab66e109e 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -22,16 +22,14 @@ }); }, // Return groups list. Filtered by query - // Only active groups retrieved - groups: function(query, skip_ldap, skip_groups, callback) { + groups: function(query, options, callback) { var url = Api.buildUrl(Api.groupsPath); return $.ajax({ url: url, - data: { - search: query, - skip_groups: skip_groups, - per_page: 20 - }, + data: $.extend({ + search: query, + per_page: 20 + }, options), dataType: "json" }).done(function(groups) { return callback(groups); diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7dd9adac736..7d942de0184 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,6 +13,7 @@ /*= require jquery-ui/sortable */ /*= require jquery_ujs */ /*= require jquery.endless-scroll */ +/*= require jquery.timeago */ /*= require jquery.highlight */ /*= require jquery.waitforimages */ /*= require jquery.atwho */ @@ -54,125 +55,53 @@ /*= require_directory . */ /*= require fuzzaldrin-plus */ -(function() { - window.slugify = function(text) { - return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase(); - }; - - window.ajaxGet = function(url) { - return $.ajax({ - type: "GET", - url: url, - dataType: "script" - }); - }; - - window.split = function(val) { - return val.split(/,\s*/); - }; - - window.extractLast = function(term) { - return split(term).pop(); - }; - - window.rstrip = function(val) { - if (val) { - return val.replace(/\s+$/, ''); - } else { - return val; - } - }; - - // Disable button if text field is empty - window.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) { - event_name = event_name || 'input'; - var closest_submit, field; - field = $(field_selector); - closest_submit = field.closest('form').find(button_selector); - if (rstrip(field.val()) === "") { - closest_submit.disable(); - } - return field.on(event_name, function() { - if (rstrip($(this).val()) === "") { - return closest_submit.disable(); - } else { - return closest_submit.enable(); - } - }); - }; +(function () { + document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch); + window.addEventListener('hashchange', gl.utils.shiftWindow); - // Disable button if any input field with given selector is empty - window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) { - var closest_submit, updateButtons; - closest_submit = form.find(button_selector); - updateButtons = function() { - var filled; - filled = true; - form.find('input').filter(form_selector).each(function() { - return filled = rstrip($(this).val()) !== "" || !$(this).attr('required'); - }); - if (filled) { - return closest_submit.enable(); - } else { - return closest_submit.disable(); - } - }; - updateButtons(); - return form.keyup(updateButtons); - }; - - window.sanitize = function(str) { - return str.replace(/<(?:.|\n)*?>/gm, ''); - }; - - window.shiftWindow = function() { - return scrollBy(0, -100); - }; - - document.addEventListener("page:fetch", gl.utils.cleanupBeforeFetch); - - window.addEventListener("hashchange", shiftWindow); - - window.onload = function() { + window.onload = function () { // Scroll the window to avoid the topnav bar // https://github.com/twitter/bootstrap/issues/1768 if (location.hash) { - return setTimeout(shiftWindow, 100); + return setTimeout(gl.utils.shiftWindow, 100); } }; - $(function() { - var $body, $document, $sidebarGutterToggle, $window, bootstrapBreakpoint, checkInitialSidebarSize, fitSidebarForSize, flash; - $document = $(document); - $window = $(window); - $body = $('body'); + $(function () { + var $body = $('body'); + var $document = $(document); + var $window = $(window); + var $sidebarGutterToggle = $('.js-sidebar-toggle'); + var $flash = $('.flash-container'); + var bootstrapBreakpoint = bp.getBreakpointSize(); + var checkInitialSidebarSize; + var fitSidebarForSize; // Set the default path for all cookies to GitLab's root directory Cookies.defaults.path = gon.relative_url_root || '/'; gl.utils.preventDisabledButtons(); - bootstrapBreakpoint = bp.getBreakpointSize(); - $(".nav-sidebar").niceScroll({ + $('.nav-sidebar').niceScroll({ cursoropacitymax: '0.4', cursorcolor: '#FFF', - cursorborder: "1px solid #FFF" + cursorborder: '1px solid #FFF' }); - $(".js-select-on-focus").on("focusin", function() { - return $(this).select().one('mouseup', function(e) { + $('.js-select-on-focus').on('focusin', function () { + return $(this).select().one('mouseup', function (e) { return e.preventDefault(); }); // Click a .js-select-on-focus field, select the contents // Prevent a mouseup event from deselecting the input }); - $('.remove-row').bind('ajax:success', function() { + $('.remove-row').bind('ajax:success', function () { $(this).tooltip('destroy') .closest('li') .fadeOut(); }); - $('.js-remove-tr').bind('ajax:before', function() { + $('.js-remove-tr').bind('ajax:before', function () { return $(this).hide(); }); - $('.js-remove-tr').bind('ajax:success', function() { + $('.js-remove-tr').bind('ajax:success', function () { return $(this).closest('tr').fadeOut(); }); $('select.select2').select2({ @@ -180,8 +109,8 @@ // Initialize select2 selects dropdownAutoWidth: true }); - $('.js-select2').bind('select2-close', function() { - return setTimeout((function() { + $('.js-select2').bind('select2-close', function () { + return setTimeout((function () { $('.select2-container-active').removeClass('select2-container-active'); return $(':focus').blur(); }), 1); @@ -191,24 +120,24 @@ $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover'; $body.tooltip({ selector: '.has-tooltip, [data-toggle="tooltip"]', - placement: function(_, el) { + placement: function (_, el) { return $(el).data('placement') || 'bottom'; } }); - $('.trigger-submit').on('change', function() { + $('.trigger-submit').on('change', function () { return $(this).parents('form').submit(); // Form submitter }); gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); // Flash - if ((flash = $(".flash-container")).length > 0) { - flash.click(function() { + if ($flash.length > 0) { + $flash.click(function () { return $(this).fadeOut(); }); - flash.show(); + $flash.show(); } // Disable form buttons while a form is submitting - $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) { + $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) { var buttons; buttons = $('[type="submit"]', this); switch (e.type) { @@ -219,36 +148,36 @@ return buttons.enable(); } }); - $(document).ajaxError(function(e, xhrObj, xhrSetting, xhrErrorText) { - var ref; + $(document).ajaxError(function (e, xhrObj) { + var ref = xhrObj.status; if (xhrObj.status === 401) { return new Flash('You need to be logged in.', 'alert'); - } else if ((ref = xhrObj.status) === 404 || ref === 500) { + } else if (ref === 404 || ref === 500) { return new Flash('Something went wrong on our end.', 'alert'); } }); - $('.account-box').hover(function() { + $('.account-box').hover(function () { // Show/Hide the profile menu when hovering the account box return $(this).toggleClass('hover'); }); - $document.on('click', '.diff-content .js-show-suppressed-diff', function() { + $document.on('click', '.diff-content .js-show-suppressed-diff', function () { var $container; $container = $(this).parent(); $container.next('table').show(); return $container.remove(); // Commit show suppressed diff }); - $('.navbar-toggle').on('click', function() { + $('.navbar-toggle').on('click', function () { $('.header-content .title').toggle(); $('.header-content .header-logo').toggle(); $('.header-content .navbar-collapse').toggle(); return $('.navbar-toggle').toggleClass('active'); }); // Show/hide comments on diff - $body.on("click", ".js-toggle-diff-comments", function(e) { + $body.on('click', '.js-toggle-diff-comments', function (e) { var $this = $(this); - $this.toggleClass('active'); var notesHolders = $this.closest('.diff-file').find('.notes_holder'); + $this.toggleClass('active'); if ($this.hasClass('active')) { notesHolders.show().find('.hide').show(); } else { @@ -257,30 +186,27 @@ $this.trigger('blur'); return e.preventDefault(); }); - $document.off("click", '.js-confirm-danger'); - $document.on("click", '.js-confirm-danger', function(e) { - var btn, form, text; + $document.off('click', '.js-confirm-danger'); + $document.on('click', '.js-confirm-danger', function (e) { + var btn = $(e.target); + var form = btn.closest('form'); + var text = btn.data('confirm-danger-message'); e.preventDefault(); - btn = $(e.target); - text = btn.data("confirm-danger-message"); - form = btn.closest("form"); return new ConfirmDangerModal(form, text); }); - $document.on('click', 'button', function() { + $document.on('click', 'button', function () { return $(this).blur(); }); - $('input[type="search"]').each(function() { - var $this; - $this = $(this); + $('input[type="search"]').each(function () { + var $this = $(this); $this.attr('value', $this.val()); }); - $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function(e) { + $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () { var $this; $this = $(this); return $this.attr('value', $this.val()); }); - $sidebarGutterToggle = $('.js-sidebar-toggle'); - $document.off('breakpoint:change').on('breakpoint:change', function(e, breakpoint) { + $document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) { var $gutterIcon; if (breakpoint === 'sm' || breakpoint === 'xs') { $gutterIcon = $sidebarGutterToggle.find('i'); @@ -289,7 +215,7 @@ } } }); - fitSidebarForSize = function() { + fitSidebarForSize = function () { var oldBootstrapBreakpoint; oldBootstrapBreakpoint = bootstrapBreakpoint; bootstrapBreakpoint = bp.getBreakpointSize(); @@ -297,13 +223,13 @@ return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); } }; - checkInitialSidebarSize = function() { + checkInitialSidebarSize = function () { bootstrapBreakpoint = bp.getBreakpointSize(); - if (bootstrapBreakpoint === "xs" || "sm") { + if (bootstrapBreakpoint === 'xs' || 'sm') { return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); } }; - $window.off("resize.app").on("resize.app", function(e) { + $window.off('resize.app').on('resize.app', function () { return fitSidebarForSize(); }); gl.awardsHandler = new AwardsHandler(); diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 8bdb0965f99..d7cda977845 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,7 +1,7 @@ /* eslint-disable */ (function() { this.AwardsHandler = (function() { - const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence + var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence function AwardsHandler() { this.aliases = gl.emojiAliases(); $(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) { diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index 230a1b95c52..143d21adb37 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -12,7 +12,7 @@ submit.disable(); $('.js-confirm-danger-input').off('input'); $('.js-confirm-danger-input').on('input', function() { - if (rstrip($(this).val()) === project_path) { + if (gl.utils.rstrip($(this).val()) === project_path) { return submit.enable(); } else { return submit.disable(); diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 31df51ac03a..824413bf20f 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -126,8 +126,8 @@ } return { username: m.username, - title: sanitize(title), - search: sanitize(m.username + " " + m.name) + title: gl.utils.sanitize(title), + search: gl.utils.sanitize(m.username + " " + m.name) }; }); } @@ -159,7 +159,7 @@ } return { id: i.iid, - title: sanitize(i.title), + title: gl.utils.sanitize(i.title), search: i.iid + " " + i.title }; }); @@ -189,7 +189,7 @@ } return { id: m.iid, - title: sanitize(m.title), + title: gl.utils.sanitize(m.title), search: "" + m.title }; }); @@ -222,7 +222,7 @@ } return { id: m.iid, - title: sanitize(m.title), + title: gl.utils.sanitize(m.title), search: m.iid + " " + m.title }; }); @@ -240,9 +240,9 @@ var sanitizeLabelTitle; sanitizeLabelTitle = function(title) { if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) { - return "\"" + (sanitize(title)) + "\""; + return "\"" + (gl.utils.sanitize(title)) + "\""; } else { - return sanitize(title); + return gl.utils.sanitize(title); } }; return $.map(merges, function(m) { diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index 742807d93ad..ce54c34492d 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -24,8 +24,8 @@ if (isNewForm) { this.form.find('.div-dropzone').remove(); this.form.addClass('gfm-form'); - disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); // remove notify commit author checkbox for non-commit notes + gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); new DropzoneInput(this.form); autosize(this.textarea); diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index b275620c799..e3c39c895ba 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -6,15 +6,16 @@ function GroupsSelect() { $('.ajax-groups-select').each((function(_this) { return function(i, select) { - var skip_ldap, skip_groups; - skip_ldap = $(select).hasClass('skip_ldap'); + var all_available, skip_groups; + all_available = $(select).data('all-available'); skip_groups = $(select).data('skip-groups') || []; return $(select).select2({ placeholder: "Search for a group", multiple: $(select).hasClass('multiselect'), minimumInputLength: 0, query: function(query) { - return Api.groups(query.term, skip_ldap, skip_groups, function(groups) { + options = { all_available: all_available, skip_groups: skip_groups }; + return Api.groups(query.term, options, function(groups) { var data; data = { results: groups diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 21efe2d76dd..8447421195d 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -24,6 +24,81 @@ return null; } }; + + w.gl.utils.ajaxGet = function(url) { + return $.ajax({ + type: "GET", + url: url, + dataType: "script" + }); + }; + + w.gl.utils.split = function(val) { + return val.split(/,\s*/); + }; + + w.gl.utils.extractLast = function(term) { + return this.split(term).pop(); + }; + + w.gl.utils.rstrip = function rstrip(val) { + if (val) { + return val.replace(/\s+$/, ''); + } else { + return val; + } + }; + + w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) { + event_name = event_name || 'input'; + var closest_submit, field, that; + that = this; + field = $(field_selector); + closest_submit = field.closest('form').find(button_selector); + if (this.rstrip(field.val()) === "") { + closest_submit.disable(); + } + return field.on(event_name, function() { + if (that.rstrip($(this).val()) === "") { + return closest_submit.disable(); + } else { + return closest_submit.enable(); + } + }); + }; + + w.gl.utils.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) { + var closest_submit, updateButtons; + closest_submit = form.find(button_selector); + updateButtons = function() { + var filled; + filled = true; + form.find('input').filter(form_selector).each(function() { + return filled = this.rstrip($(this).val()) !== "" || !$(this).attr('required'); + }); + if (filled) { + return closest_submit.enable(); + } else { + return closest_submit.disable(); + } + }; + updateButtons(); + return form.keyup(updateButtons); + }; + + w.gl.utils.sanitize = function(str) { + return str.replace(/<(?:.|\n)*?>/gm, ''); + }; + + w.gl.utils.unbindEvents = function() { + return $(document).off('scroll'); + }; + + w.gl.utils.shiftWindow = function() { + return w.scrollBy(0, -100); + }; + + gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) { return $tooltipEl.tooltip('destroy').attr('title', newTitle).tooltip('fixTitle'); }; diff --git a/app/assets/javascripts/lib/utils/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js deleted file mode 100644 index de76cdd2ea7..00000000000 --- a/app/assets/javascripts/lib/utils/jquery.timeago.js +++ /dev/null @@ -1,182 +0,0 @@ -/* eslint-disable */ -/** - * Timeago is a jQuery plugin that makes it easy to support automatically - * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). - * - * @name timeago - * @version 1.1.0 - * @requires jQuery v1.2.3+ - * @author Ryan McGeary - * @license MIT License - http://www.opensource.org/licenses/mit-license.php - * - * For usage and examples, visit: - * http://timeago.yarp.com/ - * - * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) - */ - -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - $.timeago = function(timestamp) { - if (timestamp instanceof Date) { - return inWords(timestamp); - } else if (typeof timestamp === "string") { - return inWords($.timeago.parse(timestamp)); - } else if (typeof timestamp === "number") { - return inWords(new Date(timestamp)); - } else { - return inWords($.timeago.datetime(timestamp)); - } - }; - var $t = $.timeago; - - $.extend($.timeago, { - settings: { - refreshMillis: 60000, - allowFuture: false, - strings: { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "ago", - suffixFromNow: "from now", - seconds: "less than a minute", - minute: "about a minute", - minutes: "%d minutes", - hour: "about an hour", - hours: "about %d hours", - day: "a day", - days: "%d days", - month: "about a month", - months: "%d months", - year: "about a year", - years: "%d years", - wordSeparator: " ", - numbers: [] - } - }, - inWords: function(distanceMillis) { - var $l = this.settings.strings; - var prefix = $l.prefixAgo; - var suffix = $l.suffixAgo; - if (this.settings.allowFuture) { - if (distanceMillis < 0) { - prefix = $l.prefixFromNow; - suffix = $l.suffixFromNow; - } - } - - var seconds = Math.abs(distanceMillis) / 1000; - var minutes = seconds / 60; - var hours = minutes / 60; - var days = hours / 24; - var years = days / 365; - - function substitute(stringOrFunction, number) { - var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; - var value = ($l.numbers && $l.numbers[number]) || number; - return string.replace(/%d/i, value); - } - - var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || - seconds < 90 && substitute($l.minute, 1) || - minutes < 45 && substitute($l.minutes, Math.round(minutes)) || - minutes < 90 && substitute($l.hour, 1) || - hours < 24 && substitute($l.hours, Math.round(hours)) || - hours < 42 && substitute($l.day, 1) || - days < 30 && substitute($l.days, Math.round(days)) || - days < 45 && substitute($l.month, 1) || - days < 365 && substitute($l.months, Math.round(days / 30)) || - years < 1.5 && substitute($l.year, 1) || - substitute($l.years, Math.round(years)); - - var separator = $l.wordSeparator || ""; - if ($l.wordSeparator === undefined) { separator = " "; } - return $.trim([prefix, words, suffix].join(separator)); - }, - parse: function(iso8601) { - var s = $.trim(iso8601); - s = s.replace(/\.\d+/,""); // remove milliseconds - s = s.replace(/-/,"/").replace(/-/,"/"); - s = s.replace(/T/," ").replace(/Z/," UTC"); - s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 - return new Date(s); - }, - datetime: function(elem) { - var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); - return $t.parse(iso8601); - }, - isTime: function(elem) { - // jQuery's `is()` doesn't play well with HTML5 in IE - return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); - } - }); - - // functions that can be called via $(el).timeago('action') - // init is default when no action is given - // functions are called with context of a single element - var functions = { - init: function(){ - var refresh_el = $.proxy(refresh, this); - refresh_el(); - var $s = $t.settings; - if ($s.refreshMillis > 0) { - setInterval(refresh_el, $s.refreshMillis); - } - }, - update: function(time){ - $(this).data('timeago', { datetime: $t.parse(time) }); - refresh.apply(this); - } - }; - - $.fn.timeago = function(action, options) { - var fn = action ? functions[action] : functions.init; - if(!fn){ - throw new Error("Unknown function name '"+ action +"' for timeago"); - } - // each over objects here and call the requested function - this.each(function(){ - fn.call(this, options); - }); - return this; - }; - - function refresh() { - var data = prepareData(this); - if (!isNaN(data.datetime)) { - $(this).text(inWords(data.datetime)); - } - return this; - } - - function prepareData(element) { - element = $(element); - if (!element.data("timeago")) { - element.data("timeago", { datetime: $t.datetime(element) }); - var text = $.trim(element.text()); - if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { - element.attr("title", text); - } - } - return element.data("timeago"); - } - - function inWords(date) { - return $t.inWords(distance(date)); - } - - function distance(date) { - return (new Date().getTime() - date.getTime()); - } - - // fix for IE6 suckage - document.createElement("abbr"); - document.createElement("time"); -})); diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 371abd09e78..895bc10784f 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -11,7 +11,7 @@ $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); $('.js-member-update-control').off('change').on('change', this.formSubmit); $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); - disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); + gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } removeRow(e) { diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index 40575caa57f..0d3fb31a9cf 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -45,7 +45,9 @@ }; ProjectNew.prototype.toggleRepoVisibility = function () { - var $repoAccessLevel = $('.js-repo-access-level select'); + var $repoAccessLevel = $('.js-repo-access-level select'), + containerRegistry = document.querySelectorAll('.js-container-registry')[0], + containerRegistryCheckbox = document.getElementById('project_container_registry_enabled'); this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']") .nextAll() @@ -70,8 +72,17 @@ if (selectedVal) { this.$repoSelects.removeClass('disabled'); + + if (containerRegistry) { + containerRegistry.style.display = ''; + } } else { this.$repoSelects.addClass('disabled'); + + if (containerRegistry) { + containerRegistry.style.display = 'none'; + containerRegistryCheckbox.checked = false; + } } }.bind(this)); }; diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index b74b4ae68ff..e1acf3c8232 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -24,7 +24,7 @@ data = groups.concat(projects); return finalCallback(data); }; - return Api.groups(term, false, false, groupsCallback); + return Api.groups(term, {}, groupsCallback); }; } else { projectsCallback = finalCallback; @@ -73,7 +73,7 @@ data = groups.concat(projects); return finalCallback(data); }; - return Api.groups(query.term, false, false, groupsCallback); + return Api.groups(query.term, {}, groupsCallback); }; } else { projectsCallback = finalCallback; diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 6c2389f202f..d79e6f014f6 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -11,7 +11,7 @@ filterable: true, fieldName: 'group_id', data: function(term, callback) { - return Api.groups(term, false, false, function(data) { + return Api.groups(term, {}, function(data) { data.unshift({ name: 'Any' }); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 3847278e80a..7a2221dbaf5 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -23,6 +23,8 @@ $dropdown = $(dropdown); options.projectId = $dropdown.data('project-id'); options.showCurrentUser = $dropdown.data('current-user'); + options.todoFilter = $dropdown.data('todo-filter'); + options.todoStateFilter = $dropdown.data('todo-state-filter'); showNullUser = $dropdown.data('null-user'); showMenuAbove = $dropdown.data('showMenuAbove'); showAnyUser = $dropdown.data('any-user'); @@ -394,6 +396,8 @@ project_id: options.projectId || null, group_id: options.groupId || null, skip_ldap: options.skipLdap || null, + todo_filter: options.todoFilter || null, + todo_state_filter: options.todoStateFilter || null, current_user: options.showCurrentUser || null, push_code_to_protected_branches: options.pushCodeToProtectedBranches || null, author_id: options.authorId || null, diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index baa38ab60c8..3e34ec98427 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -36,7 +36,7 @@ color: $dropdown-toggle-color; font-size: 15px; text-align: left; - border: 1px solid $dropdown-toggle-border-color; + border: 1px solid $border-color; border-radius: $border-radius-base; outline: 0; text-overflow: ellipsis; @@ -45,11 +45,9 @@ .fa { position: absolute; - top: 50%; - right: 6px; - margin-top: -6px; + top: 10px; + right: 8px; color: $dropdown-toggle-icon-color; - font-size: 10px; &.fa-spinner { font-size: 16px; diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index ecdf0be1a05..13749f1b7bd 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -27,9 +27,9 @@ height: 0; margin-left: 2px; vertical-align: middle; - border-top: $caret-width-base dashed; - border-right: $caret-width-base solid transparent; - border-left: $caret-width-base solid transparent; + border-top: 5px dashed; + border-right: 5px solid transparent; + border-left: 5px solid transparent; color: $gray-darkest; } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 52d6a39bd59..98a84351a3d 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -164,7 +164,22 @@ .branch-commit { color: $gl-gray; - .commit-id, + .commit-icon { + text-align: center; + display: inline-block; + + svg { + height: 14px; + width: 14px; + vertical-align: middle; + fill: $table-text-gray; + } + } + + .commit-id { + color: $gl-link-color; + } + .commit-row-message { color: $gl-gray; } diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index cb8cefaca97..778126bcfb7 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -55,6 +55,10 @@ float: left; } + .file-buttons { + font-size: 0; + } + .select2 { float: right; } diff --git a/app/assets/stylesheets/pages/icons.scss b/app/assets/stylesheets/pages/icons.scss new file mode 100644 index 00000000000..407c8db211d --- /dev/null +++ b/app/assets/stylesheets/pages/icons.scss @@ -0,0 +1,12 @@ +// CI icon colors + +.ci-status-icon { + &-created { + fill: $gray-darkest; + } + + &-skipped, + &-canceled { + fill: $gl-text-color; + } +} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index a8e8bbcb208..bf3cb6e7ad9 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -85,6 +85,11 @@ } .commit-link { + a { + &:focus { + text-decoration: none; + } + } .ci-status { @@ -439,7 +444,7 @@ } .grouped-pipeline-dropdown { - padding: 8px 0; + padding: 0; width: 186px; left: auto; right: -197px; @@ -448,6 +453,14 @@ ul { max-height: 245px; overflow: auto; + + li:first-child { + padding-top: 8px; + } + + li:last-child { + padding-bottom: 8px; + } } a { diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 01426e28e92..92997eae8b9 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -6,7 +6,8 @@ white-space: nowrap; border-radius: 4px; - &:hover { + &:hover, + &:focus { text-decoration: none; } diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 6ef7cf0bae6..86e808314f4 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -116,8 +116,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :metrics_packet_size, :send_user_confirmation_email, :container_registry_token_expire_delay, - :repository_storage, :enabled_git_access_protocol, + repository_storages: [], restricted_visibility_levels: [], import_sources: [], disabled_oauth_sign_in_sources: [] diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index b48668eea87..daa82336208 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -11,9 +11,13 @@ class AutocompleteController < ApplicationController @users = @users.reorder(:name) @users = @users.page(params[:page]) + if params[:todo_filter].present? + @users = @users.todo_authors(current_user.id, params[:todo_state_filter]) + end + if params[:search].blank? # Include current user if available to filter by "Me" - if params[:current_user] && current_user + if params[:current_user].present? && current_user @users = [*@users, current_user] end diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index ae060abee5c..9eaf26a0dbf 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -7,7 +7,7 @@ class Projects::GroupLinksController < Projects::ApplicationController @group_links = project.project_group_links.all @skip_groups = @group_links.pluck(:group_id) - @skip_groups << project.group.try(:id) + @skip_groups << project.namespace_id unless project.personal? end def create diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 6229384817b..45a567a1eba 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -93,11 +93,11 @@ module ApplicationSettingsHelper end end - def repository_storage_options_for_select + def repository_storages_options_for_select options = Gitlab.config.repositories.storages.map do |name, path| ["#{name} - #{path}", name] end - options_for_select(options, @application_setting.repository_storage) + options_for_select(options, @application_setting.repository_storages) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 3decedace4f..895c3d728ad 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -47,8 +47,10 @@ module CiStatusHelper 'icon_play' when 'created' 'icon_status_created' + when 'skipped' + 'icon_status_skipped' else - 'icon_status_cancel' + 'icon_status_canceled' end custom_icon(icon_name) diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 81e0b6bb5ae..cbab1fd5967 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -43,7 +43,7 @@ module DropdownsHelper default_label = data_attr[:default_label] content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}") - output << icon('chevron-down') + output << icon('caret-down') output.html_safe end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c99aa7772bb..6e7a90e7d9c 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -18,6 +18,7 @@ class ApplicationSetting < ActiveRecord::Base serialize :disabled_oauth_sign_in_sources, Array serialize :domain_whitelist, Array serialize :domain_blacklist, Array + serialize :repository_storages cache_markdown_field :sign_in_text cache_markdown_field :help_page_text @@ -74,9 +75,8 @@ class ApplicationSetting < ActiveRecord::Base presence: true, numericality: { only_integer: true, greater_than: 0 } - validates :repository_storage, - presence: true, - inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } } + validates :repository_storages, presence: true + validate :check_repository_storages validates :enabled_git_access_protocol, inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true } @@ -166,7 +166,7 @@ class ApplicationSetting < ActiveRecord::Base disabled_oauth_sign_in_sources: [], send_user_confirmation_email: false, container_registry_token_expire_delay: 5, - repository_storage: 'default', + repository_storages: ['default'], user_default_external: false, ) end @@ -201,6 +201,29 @@ class ApplicationSetting < ActiveRecord::Base self.domain_blacklist_raw = file.read end + def repository_storages + value = read_attribute(:repository_storages) + value = [value] if value.is_a?(String) + value = [] if value.nil? + + value + end + + # repository_storage is still required in the API. Remove in 9.0 + def repository_storage + repository_storages.first + end + + def repository_storage=(value) + self.repository_storages = [value] + end + + # Choose one of the available repository storage options. Currently all have + # equal weighting. + def pick_repository_storage + repository_storages.sample + end + def runners_registration_token ensure_runners_registration_token! end @@ -208,4 +231,12 @@ class ApplicationSetting < ActiveRecord::Base def health_check_access_token ensure_health_check_access_token! end + + private + + def check_repository_storages + invalid = repository_storages - Gitlab.config.repositories.storages.keys + errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless + invalid.empty? + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 0397c57f935..6b8ac3fb48b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -441,11 +441,11 @@ class MergeRequest < ActiveRecord::Base end def should_remove_source_branch? - merge_params['should_remove_source_branch'].present? + Gitlab::Utils.to_boolean(merge_params['should_remove_source_branch']) end def force_remove_source_branch? - merge_params['force_remove_source_branch'].present? + Gitlab::Utils.to_boolean(merge_params['force_remove_source_branch']) end def remove_source_branch? diff --git a/app/models/project.rb b/app/models/project.rb index d5512dfaf9c..cf931f64c03 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,7 +28,7 @@ class Project < ActiveRecord::Base default_value_for :archived, false default_value_for :visibility_level, gitlab_config_features.visibility_level default_value_for :container_registry_enabled, gitlab_config_features.container_registry - default_value_for(:repository_storage) { current_application_settings.repository_storage } + default_value_for(:repository_storage) { current_application_settings.pick_repository_storage } default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } default_value_for :issues_enabled, gitlab_config_features.issues default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests diff --git a/app/models/user.rb b/app/models/user.rb index af3c0b7dc02..65e96ee6b2e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -173,6 +173,7 @@ class User < ActiveRecord::Base 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 :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) } def self.with_two_factor joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id"). diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index c4c68cd7891..28003e5f509 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -353,9 +353,9 @@ %fieldset %legend Repository Storage .form-group - = f.label :repository_storage, 'Storage path for new projects', class: 'control-label col-sm-2' + = f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2' .col-sm-10 - = f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control' + = f.select :repository_storages, repository_storages_options_for_select, {include_hidden: false}, multiple: true, class: 'form-control' .help-block Manage repository storage paths. Learn more in the = succeed "." do diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 676812121d7..824edd171f3 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,7 +1,7 @@ - @no_container = true - page_title "Logs" - loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - Gitlab::ProductionLogger, Gitlab::SidekiqLogger, + Gitlab::EnvironmentLogger, Gitlab::SidekiqLogger, Gitlab::RepositoryCheckLogger] = render 'admin/background_jobs/head' diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 0404d0728ea..bdea1064096 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{current_user.name} issues" - xml.link href: issues_dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" + xml.link href: url_for(params), rel: "self", type: "application/atom+xml" xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html" xml.id issues_dashboard_url xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any? diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 1eec4db45a0..3caaf827ff5 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -2,13 +2,13 @@ - header_title "Issues", issues_dashboard_path(assignee_id: current_user.id) = content_for :meta_tags do - if current_user - = auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues") + = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{current_user.name} issues") .top-area = render 'shared/issuable/nav', type: :issues .nav-controls - if current_user - = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do + = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do = icon('rss') %span.icon-label Subscribe diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 2411cc45724..e247eebc3fc 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -37,7 +37,7 @@ - if params[:author_id].present? = hidden_field_tag(:author_id, params[:author_id]) = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', - placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } }) + placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } }) .filter-item.inline - if params[:type].present? = hidden_field_tag(:type, params[:type]) diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index b1628040325..0cc6466d34e 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@group.name} issues" - xml.link href: issues_group_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" + xml.link href: url_for(params), rel: "self", type: "application/atom+xml" xml.link href: issues_group_url, rel: "alternate", type: "text/html" xml.id issues_group_url xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any? diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 4434f1cbd35..dc6c1bb69de 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,13 +1,13 @@ - page_title "Issues" = content_for :meta_tags do - if current_user - = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues") + = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues") .top-area = render 'shared/issuable/nav', type: :issues .nav-controls - if current_user - = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do + = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do = icon('rss') %span.icon-label Subscribe diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 6922f1e153f..afd9958f073 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -26,8 +26,8 @@ Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki. - - if current_application_settings.sign_in_text.present? - = markdown_field(current_application_settings, :sign_in_text) + - if current_application_settings.sign_in_text.present? + = markdown_field(current_application_settings, :sign_in_text) %hr.footer-fixed .container.footer-container diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index d4f59764a70..4a6aa92e3f3 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -14,13 +14,13 @@ = text_field_tag 'file_name', params[:file_name], placeholder: "File name", required: true, class: 'form-control new-file-name' - .pull-right + .pull-right.file-buttons .license-selector.js-license-selector-wrap.hidden - = dropdown_tag("Choose a License template", options: { toggle_class: 'js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) + = dropdown_tag("Choose a License template", options: { toggle_class: 'btn js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) .gitignore-selector.js-gitignore-selector-wrap.hidden - = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) + = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden - = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) + = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'btn js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) = button_tag class: 'soft-wrap-toggle btn', type: 'button' do %span.no-wrap = custom_icon('icon_no_wrap') diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index b1f50eb5f34..57a27ec904e 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -26,6 +26,6 @@ :javascript - disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); + gl.utils.disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); new BlobFileDropzone($('.js-upload-blob-form'), '#{method}'); new NewCommitForm($('.js-upload-blob-form')) diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index d54c76ff9c8..de607772df6 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -1,4 +1,6 @@ .branch-commit + .icon-container.commit-icon + = custom_icon("icon_commit") = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-id monospace" · %span.str-truncated diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 840f468dc05..1f748d73d06 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -41,7 +41,7 @@ - else Cant find HEAD commit for this branch - - stages_status = pipeline.statuses.relevant.latest.stages_status + - stages_status = pipeline.statuses.latest.stages_status %td.stage-cell - stages.each do |stage| - status = stages_status[stage] diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index c40ad06969e..a5422966617 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -102,7 +102,7 @@ = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') - if Gitlab.config.registry.enabled - .form-group + .form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) } .checkbox = f.label :container_registry_enabled do = f.check_box :container_registry_enabled @@ -290,4 +290,4 @@ Saving project. %p Please wait a moment, this page will automatically refresh when ready. -= render 'shared/confirm_modal', phrase: @project.path
\ No newline at end of file += render 'shared/confirm_modal', phrase: @project.path diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 36957560de0..a0df0db77c5 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@project.name} issues" - xml.link href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" + xml.link href: url_for(params), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.id namespace_project_issues_url(@project.namespace, @project) xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any? diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index cc57cfdb342..c493ff3585b 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -8,7 +8,7 @@ = content_for :meta_tags do - if current_user - = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") + = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@project.name} issues") %div{ class: (container_class) } - if @project.issues.any? @@ -16,7 +16,7 @@ = render 'shared/issuable/nav', type: :issues .nav-controls - if current_user - = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do + = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10' do = icon('rss') %span.icon-label Subscribe diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 69a3bc5f046..4de95036eef 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -12,7 +12,7 @@ = render 'projects/last_push' = render "home_panel" -- if @project.feature_available?(:repository, current_user) +- if current_user && can?(current_user, :download_code, @project) %nav.project-stats{ class: container_class } %ul.nav %li diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index 0f7d629ab98..21e378b8735 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -37,5 +37,5 @@ :javascript // Load last commit log for each file in tree $('#tree-slider').waitForImages(function() { - ajaxGet("#{escape_javascript(@logs_path)}"); + gl.utils.ajaxGet("#{escape_javascript(@logs_path)}"); }); diff --git a/app/views/shared/icons/_icon_status_cancel.svg b/app/views/shared/icons/_icon_status_canceled.svg index fd1ebbcbabd..1b2d0891244 100644 --- a/app/views/shared/icons/_icon_status_cancel.svg +++ b/app/views/shared/icons/_icon_status_canceled.svg @@ -1,4 +1,4 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"> +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-canceled" viewBox="0 0 14 14"> <g fill="#5C5C5C" fill-rule="evenodd"> <path d="M12.5,7 C12.5,3.96243388 10.0375661,1.5 7,1.5 C3.96243388,1.5 1.5,3.96243388 1.5,7 C1.5,10.0375661 3.96243388,12.5 7,12.5 C10.0375661,12.5 12.5,10.0375661 12.5,7 Z M0,7 C0,3.13400675 3.13400675,0 7,0 C10.8659932,0 14,3.13400675 14,7 C14,10.8659932 10.8659932,14 7,14 C3.13400675,14 0,10.8659932 0,7 Z"/> <rect width="8" height="2" x="3" y="6" transform="rotate(45 7 7)" rx=".5"/> diff --git a/app/views/shared/icons/_icon_status_created.svg b/app/views/shared/icons/_icon_status_created.svg index 1f5c3b51b03..dca5d289767 100644 --- a/app/views/shared/icons/_icon_status_created.svg +++ b/app/views/shared/icons/_icon_status_created.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-created" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg> diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg new file mode 100644 index 00000000000..014ca86b61b --- /dev/null +++ b/app/views/shared/icons/_icon_status_skipped.svg @@ -0,0 +1 @@ +<svg width="20" height="20" 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> diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index d410755cad1..0ace6be8f4e 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -142,6 +142,7 @@ .col-sm-10.col-sm-offset-2 .checkbox = label_tag 'merge_request[force_remove_source_branch]' do + = hidden_field_tag 'merge_request[force_remove_source_branch]', '0' = check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch? Remove source branch when merge request is accepted. diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml index 22b5a6aa11b..1d778bc88de 100644 --- a/app/views/shared/issuable/_label_dropdown.html.haml +++ b/app/views/shared/issuable/_label_dropdown.html.haml @@ -22,7 +22,7 @@ %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) } = multi_label_name(selected, "Labels") - = icon('chevron-down') + = icon('caret-down') .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable = render partial: "shared/issuable/label_page_default", locals: { title: dropdown_title, show_footer: show_footer, show_create: show_create } - if show_create && project && can?(current_user, :admin_label, project) |