Commit 25521a0a authored by Ruben Davila's avatar Ruben Davila

Merge remote-tracking branch 'ce/8-11-stable' into 8-11-stable-ee

parents 4894540a 4bb1db90
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased) v 8.11.0 (unreleased)
- Use test coverage value from the latest successful pipeline in badge. !5862
- Add test coverage report badge. !5708 - Add test coverage report badge. !5708
- Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar) - Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
- Add Koding (online IDE) integration
- Ability to specify branches for Pivotal Tracker integration (Egor Lynko) - Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres) - Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres) - Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix adding line comments on the initial commit to a repo !5900
- Fix the title of the toggle dropdown button. !5515 (herminiotorres) - Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz) - Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz)
- Update to Ruby 2.3.1. !4948 - Update to Ruby 2.3.1. !4948
...@@ -17,6 +20,7 @@ v 8.11.0 (unreleased) ...@@ -17,6 +20,7 @@ v 8.11.0 (unreleased)
- API: Endpoints for enabling and disabling deploy keys - API: Endpoints for enabling and disabling deploy keys
- API: List access requests, request access, approve, and deny access requests to a project or a group. !4833 - API: List access requests, request access, approve, and deny access requests to a project or a group. !4833
- Use long options for curl examples in documentation !5703 (winniehell) - Use long options for curl examples in documentation !5703 (winniehell)
- Added tooltip listing label names to the labels value in the collapsed issuable sidebar
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell) - Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository - GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell) - Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
...@@ -24,6 +28,8 @@ v 8.11.0 (unreleased) ...@@ -24,6 +28,8 @@ v 8.11.0 (unreleased)
- Ignore URLs starting with // in Markdown links !5677 (winniehell) - Ignore URLs starting with // in Markdown links !5677 (winniehell)
- Fix CI status icon link underline (ClemMakesApps) - Fix CI status icon link underline (ClemMakesApps)
- The Repository class is now instrumented - The Repository class is now instrumented
- Fix commit mention font inconsistency (ClemMakesApps)
- Do not escape URI when extracting path !5878 (winniehell)
- Fix filter label tooltip HTML rendering (ClemMakesApps) - Fix filter label tooltip HTML rendering (ClemMakesApps)
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive - Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- Expand commit message width in repo view (ClemMakesApps) - Expand commit message width in repo view (ClemMakesApps)
...@@ -33,6 +39,7 @@ v 8.11.0 (unreleased) ...@@ -33,6 +39,7 @@ v 8.11.0 (unreleased)
- API: Add deployment endpoints - API: Add deployment endpoints
- API: Add Play endpoint on Builds - API: Add Play endpoint on Builds
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI' - Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Show wall clock time when showing a pipeline. !5734
- Show member roles to all users on members page - Show member roles to all users on members page
- Project.visible_to_user is instrumented again - Project.visible_to_user is instrumented again
- Fix awardable button mutuality loading spinners (ClemMakesApps) - Fix awardable button mutuality loading spinners (ClemMakesApps)
...@@ -90,6 +97,7 @@ v 8.11.0 (unreleased) ...@@ -90,6 +97,7 @@ v 8.11.0 (unreleased)
- The overhead of instrumented method calls has been reduced - The overhead of instrumented method calls has been reduced
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le) - Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members` - Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Add pipeline events hook
- Bump gitlab_git to speedup DiffCollection iterations - Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich) - Rewrite description of a blocked user in admin settings. (Elias Werberich)
- Make branches sortable without push permission !5462 (winniehell) - Make branches sortable without push permission !5462 (winniehell)
...@@ -120,9 +128,11 @@ v 8.11.0 (unreleased) ...@@ -120,9 +128,11 @@ v 8.11.0 (unreleased)
- Change requests_profiles resource constraint to catch virtually any file - Change requests_profiles resource constraint to catch virtually any file
- Bump gitlab_git to lazy load compare commits - Bump gitlab_git to lazy load compare commits
- Reduce number of queries made for merge_requests/:id/diffs - Reduce number of queries made for merge_requests/:id/diffs
- Add the option to set the expiration date for the project membership when giving a user access to a project. !5599 (Adam Niedzielski)
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y) - Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- Fix bug where destroying a namespace would not always destroy projects - Fix bug where destroying a namespace would not always destroy projects
- Fix RequestProfiler::Middleware error when code is reloaded in development - Fix RequestProfiler::Middleware error when code is reloaded in development
- Allow horizontal scrolling of code blocks in issue body
- Catch what warden might throw when profiling requests to re-throw it - Catch what warden might throw when profiling requests to re-throw it
- Avoid commit lookup on diff_helper passing existing local variable to the helper method - Avoid commit lookup on diff_helper passing existing local variable to the helper method
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac) - Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
...@@ -137,6 +147,7 @@ v 8.11.0 (unreleased) ...@@ -137,6 +147,7 @@ v 8.11.0 (unreleased)
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0 - Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska) - Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
- Add pipelines tab to merge requests - Add pipelines tab to merge requests
- Fix notification_service argument error of declined invitation emails
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter - Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- Speed up todos queries by limiting the projects set we join with - Speed up todos queries by limiting the projects set we join with
- Ensure file editing in UI does not overwrite commited changes without warning user - Ensure file editing in UI does not overwrite commited changes without warning user
......
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14">
<g fill="#d6d7d9">
<path d="M8.7 0L5.3.3l3.2 6.8-3.2 6.6 3.5.3L12 6.9z"/>
<ellipse cx="1.7" cy="11.1" rx="1.7" ry="1.7"/>
<ellipse cx="1.7" cy="5.6" rx="1.7" ry="1.7"/>
</g>
</svg>
\ No newline at end of file
...@@ -27,8 +27,6 @@ ...@@ -27,8 +27,6 @@
/*= require bootstrap/tooltip */ /*= require bootstrap/tooltip */
/*= require bootstrap/popover */ /*= require bootstrap/popover */
/*= require select2 */ /*= require select2 */
/*= require ace-rails-ap */
/*= require ace/ext-searchbox */
/*= require underscore */ /*= require underscore */
/*= require dropzone */ /*= require dropzone */
/*= require mousetrap */ /*= require mousetrap */
......
/*= require_tree . */
(function() {
$(function() {
var url = $(".js-edit-blob-form").data("relative-url-root");
url += $(".js-edit-blob-form").data("assets-prefix");
var blob = new EditBlob(url, $('.js-edit-blob-form').data('blob-language'));
new NewCommitForm($('.js-edit-blob-form'));
});
}).call(this);
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
draggable: '.is-draggable', draggable: '.is-draggable',
handle: '.js-board-handle', handle: '.js-board-handle',
onEnd: (e) => { onEnd: (e) => {
document.body.classList.remove('is-dragging'); gl.issueBoards.onEnd();
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) { if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
const order = this.sortable.toArray(), const order = this.sortable.toArray(),
...@@ -72,10 +72,6 @@ ...@@ -72,10 +72,6 @@
} }
}); });
if (bp.getBreakpointSize() === 'xs') {
options.handle = '.js-board-drag-handle';
}
this.sortable = Sortable.create(this.$el.parentNode, options); this.sortable = Sortable.create(this.$el.parentNode, options);
}, },
beforeDestroy () { beforeDestroy () {
......
...@@ -63,6 +63,8 @@ ...@@ -63,6 +63,8 @@
Store.moving.issue = card.issue; Store.moving.issue = card.issue;
Store.moving.list = card.list; Store.moving.list = card.list;
gl.issueBoards.onStart();
}, },
onAdd: (e) => { onAdd: (e) => {
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue); gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
...@@ -72,10 +74,6 @@ ...@@ -72,10 +74,6 @@
} }
}); });
if (bp.getBreakpointSize() === 'xs') {
options.handle = '.js-card-drag-handle';
}
this.sortable = Sortable.create(this.$els.list, options); this.sortable = Sortable.create(this.$els.list, options);
// Scroll event on list to load more // Scroll event on list to load more
......
...@@ -2,6 +2,19 @@ ...@@ -2,6 +2,19 @@
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.onStart = () => {
$('.has-tooltip').tooltip('hide')
.tooltip('disable');
document.body.classList.add('is-dragging');
};
gl.issueBoards.onEnd = () => {
$('.has-tooltip').tooltip('enable');
document.body.classList.remove('is-dragging');
};
gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
gl.issueBoards.getBoardSortableDefaultOptions = (obj) => { gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
let defaultSortOptions = { let defaultSortOptions = {
forceFallback: true, forceFallback: true,
...@@ -9,14 +22,11 @@ ...@@ -9,14 +22,11 @@
fallbackOnBody: true, fallbackOnBody: true,
ghostClass: 'is-ghost', ghostClass: 'is-ghost',
filter: '.has-tooltip', filter: '.has-tooltip',
scrollSensitivity: 100, delay: gl.issueBoards.touchEnabled ? 100 : 0,
scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
scrollSpeed: 20, scrollSpeed: 20,
onStart () { onStart: gl.issueBoards.onStart,
document.body.classList.add('is-dragging'); onEnd: gl.issueBoards.onEnd
},
onEnd () {
document.body.classList.remove('is-dragging');
}
} }
Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; }); Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
......
...@@ -3,6 +3,7 @@ class ListLabel { ...@@ -3,6 +3,7 @@ class ListLabel {
this.id = obj.id; this.id = obj.id;
this.title = obj.title; this.title = obj.title;
this.color = obj.color; this.color = obj.color;
this.textColor = obj.text_color;
this.description = obj.description; this.description = obj.description;
this.priority = (obj.priority !== null) ? obj.priority : Infinity; this.priority = (obj.priority !== null) ? obj.priority : Infinity;
} }
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
path = page.split(':'); path = page.split(':');
shortcut_handler = null; shortcut_handler = null;
switch (page) { switch (page) {
case 'projects:boards:show':
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:issues:index': case 'projects:issues:index':
Issuable.init(); Issuable.init();
new IssuableBulkActions(); new IssuableBulkActions();
...@@ -126,10 +129,12 @@ ...@@ -126,10 +129,12 @@
new NotificationsDropdown(); new NotificationsDropdown();
break; break;
case 'groups:group_members:index': case 'groups:group_members:index':
new gl.MemberExpirationDate();
new GroupMembers(); new GroupMembers();
new UsersSelect(); new UsersSelect();
break; break;
case 'projects:project_members:index': case 'projects:project_members:index':
new gl.MemberExpirationDate();
new ProjectMembers(); new ProjectMembers();
new UsersSelect(); new UsersSelect();
break; break;
...@@ -171,6 +176,7 @@ ...@@ -171,6 +176,7 @@
new BuildArtifacts(); new BuildArtifacts();
break; break;
case 'projects:group_links:index': case 'projects:group_links:index':
new gl.MemberExpirationDate();
new GroupsSelect(); new GroupsSelect();
break; break;
case 'search:show': case 'search:show':
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
var _this; var _this;
_this = this; _this = this;
$('.js-label-select').each(function(i, dropdown) { $('.js-label-select').each(function(i, dropdown) {
var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, projectId, saveLabelData, selectedLabel, showAny, showNo; var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, projectId, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip;
$dropdown = $(dropdown); $dropdown = $(dropdown);
projectId = $dropdown.data('project-id'); projectId = $dropdown.data('project-id');
labelUrl = $dropdown.data('labels'); labelUrl = $dropdown.data('labels');
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
$block = $selectbox.closest('.block'); $block = $selectbox.closest('.block');
$form = $dropdown.closest('form'); $form = $dropdown.closest('form');
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
$value = $block.find('.value'); $value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut(); $loading = $block.find('.block-loading').fadeOut();
if (issueUpdateURL != null) { if (issueUpdateURL != null) {
...@@ -31,7 +32,11 @@ ...@@ -31,7 +32,11 @@
labelNoneHTMLTemplate = '<span class="no-value">None</span>'; labelNoneHTMLTemplate = '<span class="no-value">None</span>';
} }
$sidebarLabelTooltip.tooltip();
if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) {
new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), projectId); new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), projectId);
}
saveLabelData = function() { saveLabelData = function() {
var data, selected; var data, selected;
...@@ -52,7 +57,7 @@ ...@@ -52,7 +57,7 @@
dataType: 'JSON', dataType: 'JSON',
data: data data: data
}).done(function(data) { }).done(function(data) {
var labelCount, template; var labelCount, template, labelTooltipTitle, labelTitles;
$loading.fadeOut(); $loading.fadeOut();
$dropdown.trigger('loaded.gl.dropdown'); $dropdown.trigger('loaded.gl.dropdown');
$selectbox.hide(); $selectbox.hide();
...@@ -66,6 +71,27 @@ ...@@ -66,6 +71,27 @@
} }
$value.removeAttr('style').html(template); $value.removeAttr('style').html(template);
$sidebarCollapsedValue.text(labelCount); $sidebarCollapsedValue.text(labelCount);
if (data.labels.length) {
labelTitles = data.labels.map(function(label) {
return label.title;
});
if (labelTitles.length > 5) {
labelTitles = labelTitles.slice(0, 5);
labelTitles.push('and ' + (data.labels.length - 5) + ' more');
}
labelTooltipTitle = labelTitles.join(', ');
} else {
labelTooltipTitle = '';
$sidebarLabelTooltip.tooltip('destroy');
}
$sidebarLabelTooltip
.attr('title', labelTooltipTitle)
.tooltip('fixTitle');
$('.has-tooltip', $value).tooltip({ $('.has-tooltip', $value).tooltip({
container: 'body' container: 'body'
}); });
......
/*= require ace-rails-ap */
/*= require ace/ext-searchbox */
(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
// `js-clear-input` element, then show that element when there is a value in the
// datepicker, and make clicking on that element clear the field.
//
gl.MemberExpirationDate = function() {
function toggleClearInput() {
$(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== '');
}
var inputs = $('.js-access-expiration-date');
inputs.datepicker({
dateFormat: 'yy-mm-dd',
minDate: 1,
onSelect: toggleClearInput
});
inputs.next('.js-clear-input').on('click', function(event) {
event.preventDefault();
var input = $(this).closest('.clearable-input').find('.js-access-expiration-date');
input.datepicker('setDate', null);
toggleClearInput.call(input);
});
inputs.on('blur', toggleClearInput);
inputs.each(toggleClearInput);
};
}).call(this);
...@@ -5,9 +5,6 @@ ...@@ -5,9 +5,6 @@
return $(this).fadeOut(); return $(this).fadeOut();
}); });
} }
return ProjectMembers; return ProjectMembers;
})(); })();
}).call(this); }).call(this);
/*= require_tree . */
(function() {
$(function() {
var editor = ace.edit("editor")
$(".snippet-form-holder form").on('submit', function() {
$(".snippet-file-content").val(editor.getValue());
});
});
}).call(this);
...@@ -14,12 +14,20 @@ ...@@ -14,12 +14,20 @@
margin-top: 0; margin-top: 0;
} }
// Single code lines should wrap
code { code {
font-family: $monospace_font; font-family: $monospace_font;
white-space: pre; white-space: pre-wrap;
word-wrap: normal; word-wrap: normal;
} }
// Multi-line code blocks should scroll horizontally
pre {
code {
white-space: pre;
}
}
kbd { kbd {
display: inline-block; display: inline-block;
padding: 3px 5px; padding: 3px 5px;
......
...@@ -8,9 +8,13 @@ ...@@ -8,9 +8,13 @@
} }
.is-dragging { .is-dragging {
// Important because plugin sets inline CSS
opacity: 1!important;
* { * {
cursor: -webkit-grabbing; // !important to make sure no style can override this when dragging
cursor: grabbing; cursor: -webkit-grabbing!important;
cursor: grabbing!important;
} }
} }
...@@ -101,8 +105,8 @@ ...@@ -101,8 +105,8 @@
.board { .board {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
min-width: calc(100vw - 15px); min-width: calc(85vw - 15px);
max-width: calc(100vw - 15px); max-width: calc(85vw - 15px);
margin-bottom: 25px; margin-bottom: 25px;
padding-right: ($gl-padding / 2); padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2); padding-left: ($gl-padding / 2);
...@@ -154,14 +158,6 @@ ...@@ -154,14 +158,6 @@
padding: $gl-padding; padding: $gl-padding;
font-size: 1em; font-size: 1em;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
.board-mobile-handle {
position: relative;
left: 0;
top: 1px;
margin-top: 0;
margin-right: 5px;
}
} }
.board-search-container { .board-search-container {
...@@ -254,11 +250,6 @@ ...@@ -254,11 +250,6 @@
opacity: 0.3; opacity: 0.3;
} }
.is-dragging {
// Important because plugin sets inline CSS
opacity: 1!important;
}
.card { .card {
position: relative; position: relative;
width: 100%; width: 100%;
...@@ -269,12 +260,8 @@ ...@@ -269,12 +260,8 @@
list-style: none; list-style: none;
&.user-can-drag { &.user-can-drag {
padding-left: ($gl-padding * 2);
@media (min-width: $screen-sm-min) {
padding-left: $gl-padding; padding-left: $gl-padding;
} }
}
&:not(:last-child) { &:not(:last-child) {
margin-bottom: 5px; margin-bottom: 5px;
...@@ -294,17 +281,6 @@ ...@@ -294,17 +281,6 @@
} }
} }
.board-mobile-handle {
position: absolute;
left: 10px;
top: 50%;
margin-top: (-15px / 2);
@media (min-width: $screen-sm-min) {
display: none;
}
}
.card-title { .card-title {
margin: 0; margin: 0;
font-size: 1em; font-size: 1em;
...@@ -316,6 +292,7 @@ ...@@ -316,6 +292,7 @@
.card-footer { .card-footer {
margin-top: 5px; margin-top: 5px;
line-height: 25px;
.label { .label {
margin-right: 4px; margin-right: 4px;
......
...@@ -168,7 +168,6 @@ ...@@ -168,7 +168,6 @@
text-overflow: ellipsis; text-overflow: ellipsis;
&:hover { &:hover {
background-color: $row-hover;
color: $gl-text-color; color: $gl-text-color;
} }
} }
...@@ -190,6 +189,10 @@ ...@@ -190,6 +189,10 @@
display: block; display: block;
} }
} }
&:hover {
background-color: $row-hover;
}
} }
} }
} }
......
...@@ -66,6 +66,15 @@ ...@@ -66,6 +66,15 @@
margin-left: 8px; margin-left: 8px;
} }
} }
.ci-status-link {
svg {
position: relative;
top: 2px;
margin: 0 2px 0 3px;
}
}
} }
.ci-status-link { .ci-status-link {
......
...@@ -34,11 +34,4 @@ ...@@ -34,11 +34,4 @@
} }
} }
} }
.wiki {
code {
white-space: pre-wrap;
word-break: keep-all;
}
}
} }
...@@ -374,3 +374,10 @@ ...@@ -374,3 +374,10 @@
} }
} }
} }
.merge-request-details {
.title {
margin-bottom: 20px;
}
}
...@@ -300,6 +300,17 @@ ...@@ -300,6 +300,17 @@
&.playable { &.playable {
background-color: $gray-light; background-color: $gray-light;
svg {
height: 12px;
width: 12px;
position: relative;
top: 1px;
path {
fill: $layout-link-gray;
}
}
} }
.build-content { .build-content {
...@@ -319,10 +330,6 @@ ...@@ -319,10 +330,6 @@
margin-right: 5px; margin-right: 5px;
} }
.fa {
font-size: 13px;
}
// Connect first build in each stage with right horizontal line // Connect first build in each stage with right horizontal line
&:first-child { &:first-child {
&::after { &::after {
......
...@@ -739,3 +739,29 @@ pre.light-well { ...@@ -739,3 +739,29 @@ pre.light-well {
width: 300px; width: 300px;
} }
} }
.clearable-input {
position: relative;
.clear-icon {
@extend .fa-times;
display: none;
position: absolute;
right: 7px;
top: 7px;
color: $location-icon-color;
&:before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
}
}
&.has-value {
.clear-icon {
cursor: pointer;
display: block;
}
}
}
...@@ -113,6 +113,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -113,6 +113,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:sentry_dsn, :sentry_dsn,
:akismet_enabled, :akismet_enabled,
:akismet_api_key, :akismet_api_key,
:koding_enabled,
:koding_url,
:email_author_in_body, :email_author_in_body,
:repository_checks_enabled, :repository_checks_enabled,
:metrics_packet_size, :metrics_packet_size,
......
...@@ -42,7 +42,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -42,7 +42,7 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def members_update def members_update
@group.add_users(params[:user_ids].split(','), params[:access_level], current_user) @group.add_users(params[:user_ids].split(','), params[:access_level], current_user: current_user)
redirect_to [:admin, @group], notice: 'Users were successfully added.' redirect_to [:admin, @group], notice: 'Users were successfully added.'
end end
......
...@@ -7,11 +7,16 @@ module ServiceParams ...@@ -7,11 +7,16 @@ module ServiceParams
:build_key, :server, :teamcity_url, :drone_url, :build_type, :build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels, :colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events, # We're using `issues_events` and `merge_requests_events`
:note_events, :build_events, :wiki_page_events, # in the view so we still need to explicitly state them
:notify_only_broken_builds, :add_pusher, # here. `Service#event_names` would only give
:send_from_committer_email, :disable_diffs, :external_wiki_url, # `issue_events` and `merge_request_events` (singular!)
:notify, :color, # See app/helpers/services_helper.rb for how we
# make those event names plural as special case.
:issues_events, :merge_requests_events,
:notify_only_broken_builds, :notify_only_broken_pipelines,
:add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id] :jira_issue_transition_id]
...@@ -19,9 +24,7 @@ module ServiceParams ...@@ -19,9 +24,7 @@ module ServiceParams
FILTER_BLANK_PARAMS = [:password] FILTER_BLANK_PARAMS = [:password]
def service_params def service_params
dynamic_params = [] dynamic_params = @service.event_channel_names + @service.event_names
dynamic_params.concat(@service.event_channel_names)
service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params) service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
if service_params[:service].is_a?(Hash) if service_params[:service].is_a?(Hash)
......
...@@ -21,10 +21,15 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -21,10 +21,15 @@ class Groups::GroupMembersController < Groups::ApplicationController
end end
def create def create
access_level = params[:access_level]
user_ids = params[:user_ids].split(',') user_ids = params[:user_ids].split(',')
@group.add_users(user_ids, access_level, current_user) @group.add_users(
user_ids,
params[:access_level],
current_user: current_user,
expires_at: params[:expires_at]
)
group_members = @group.group_members.where(user_id: user_ids) group_members = @group.group_members.where(user_id: user_ids)
group_members.each do |group_member| group_members.each do |group_member|
...@@ -76,7 +81,7 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -76,7 +81,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
protected protected
def member_params def member_params
params.require(:group_member).permit(:access_level, :user_id) params.require(:group_member).permit(:access_level, :user_id, :expires_at)
end end
# MembershipActions concern # MembershipActions concern
......
class KodingController < ApplicationController
before_action :check_integration!, :authenticate_user!, :reject_blocked!
layout 'koding'
def index
path = File.join(Rails.root, 'doc/integration/koding-usage.md')
@markdown = File.read(path)
end
private
def check_integration!
render_404 unless current_application_settings.koding_enabled?
end
end
...@@ -12,7 +12,7 @@ module Projects ...@@ -12,7 +12,7 @@ module Projects
only: [:iid, :title, :confidential], only: [:iid, :title, :confidential],
include: { include: {
assignee: { only: [:id, :name, :username], methods: [:avatar_url] }, assignee: { only: [:id, :name, :username], methods: [:avatar_url] },
labels: { only: [:id, :title, :description, :color, :priority] } labels: { only: [:id, :title, :description, :color, :priority], methods: [:text_color] }
}) })
end end
......
...@@ -11,7 +11,9 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -11,7 +11,9 @@ class Projects::GroupLinksController < Projects::ApplicationController
return render_404 unless can?(current_user, :read_group, group) return render_404 unless can?(current_user, :read_group, group)
project.project_group_links.create( project.project_group_links.create(
group: group, group_access: params[:link_group_access] group: group,
group_access: params[:link_group_access],
expires_at: params[:expires_at]
) )
redirect_to namespace_project_group_links_path(project.namespace, project) redirect_to namespace_project_group_links_path(project.namespace, project)
......
...@@ -56,6 +56,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -56,6 +56,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params def hook_params
params.require(:hook).permit( params.require(:hook).permit(
:build_events, :build_events,
:pipeline_events,
:enable_ssl_verification, :enable_ssl_verification,
:issues_events, :issues_events,
:merge_requests_events, :merge_requests_events,
......
...@@ -36,7 +36,13 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -36,7 +36,13 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end end
def create def create
@project.team.add_users(params[:user_ids].split(','), params[:access_level], current_user) @project.team.add_users(
params[:user_ids].split(','),
params[:access_level],
expires_at: params[:expires_at],
current_user: current_user
)
members = @project.project_members.where(user_id: params[:user_ids].split(',')) members = @project.project_members.where(user_id: params[:user_ids].split(','))
members.each do |member| members.each do |member|
...@@ -105,7 +111,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -105,7 +111,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
protected protected
def member_params def member_params
params.require(:project_member).permit(:user_id, :access_level) params.require(:project_member).permit(:user_id, :access_level, :expires_at)
end end
# MembershipActions concern # MembershipActions concern
......
...@@ -35,6 +35,10 @@ module ApplicationSettingsHelper ...@@ -35,6 +35,10 @@ module ApplicationSettingsHelper
current_application_settings.akismet_enabled? current_application_settings.akismet_enabled?
end end
def koding_enabled?
current_application_settings.koding_enabled?
end
def allowed_protocols_present? def allowed_protocols_present?
current_application_settings.enabled_git_access_protocol.present? current_application_settings.enabled_git_access_protocol.present?
end end
......
...@@ -217,4 +217,12 @@ module BlobHelper ...@@ -217,4 +217,12 @@ module BlobHelper
def gitlab_ci_ymls def gitlab_ci_ymls
@gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names @gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names
end end
def blob_editor_paths
{
'relative-url-root' => Rails.application.config.relative_url_root,
'assets-prefix' => Gitlab::Application.config.assets.prefix,
'blob-language' => @blob && @blob.language.try(:ace_mode)
}
end
end end
...@@ -39,7 +39,7 @@ module CiStatusHelper ...@@ -39,7 +39,7 @@ module CiStatusHelper
when 'running' when 'running'
'icon_status_running' 'icon_status_running'
when 'play' when 'play'
return icon('play fw') 'icon_play'
when 'created' when 'created'
'icon_status_pending' 'icon_status_pending'
else else
......
...@@ -72,6 +72,15 @@ module IssuablesHelper ...@@ -72,6 +72,15 @@ module IssuablesHelper
end end
end end
def issuable_labels_tooltip(labels, limit: 5)
first, last = labels.partition.with_index{ |_, i| i < limit }
label_names = first.collect(&:name)
label_names << "and #{last.size} more" unless last.empty?
label_names.join(', ')
end
private private
def sidebar_gutter_collapsed? def sidebar_gutter_collapsed?
......
...@@ -245,6 +245,60 @@ module ProjectsHelper ...@@ -245,6 +245,60 @@ module ProjectsHelper
) )
end end
def add_koding_stack_path(project)
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch || 'master',
file_name: '.koding.yml',
commit_message: "Add Koding stack script",
content: <<-CONTENT.strip_heredoc
provider:
aws:
access_key: '${var.aws_access_key}'
secret_key: '${var.aws_secret_key}'
resource:
aws_instance:
#{project.path}-vm:
instance_type: t2.nano
user_data: |-
# Created by GitLab UI for :>
echo _KD_NOTIFY_@Installing Base packages...@
apt-get update -y
apt-get install git -y
echo _KD_NOTIFY_@Cloning #{project.name}...@
export KODING_USER=${var.koding_user_username}
export REPO_URL=#{root_url}${var.koding_queryString_repo}.git
export BRANCH=${var.koding_queryString_branch}
sudo -i -u $KODING_USER git clone $REPO_URL -b $BRANCH
echo _KD_NOTIFY_@#{project.name} cloned.@
CONTENT
)
end
def koding_project_url(project = nil, branch = nil, sha = nil)
if project
import_path = "/Home/Stacks/import"
repo = project.path_with_namespace
branch ||= project.default_branch
sha ||= project.commit.short_id
path = "#{import_path}?repo=#{repo}&branch=#{branch}&sha=#{sha}"
return URI.join(current_application_settings.koding_url, path).to_s
end
current_application_settings.koding_url
end
def contribution_guide_path(project) def contribution_guide_path(project)
if project && contribution_guide = project.repository.contribution_guide if project && contribution_guide = project.repository.contribution_guide
namespace_project_blob_path( namespace_project_blob_path(
......
...@@ -15,20 +15,9 @@ module TimeHelper ...@@ -15,20 +15,9 @@ module TimeHelper
"#{from.to_s(:short)} - #{to.to_s(:short)}" "#{from.to_s(:short)} - #{to.to_s(:short)}"
end end
def duration_in_numbers(finished_at, started_at) def duration_in_numbers(duration)
interval = interval_in_seconds(started_at, finished_at) time_format = duration < 1.hour ? "%M:%S" : "%H:%M:%S"
time_format = interval < 1.hour ? "%M:%S" : "%H:%M:%S"
Time.at(interval).utc.strftime(time_format) Time.at(duration).utc.strftime(time_format)
end
private
def interval_in_seconds(started_at, finished_at = nil)
if started_at && finished_at
finished_at.to_i - started_at.to_i
elsif started_at
Time.now.to_i - started_at.to_i
end
end end
end end
...@@ -55,6 +55,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -55,6 +55,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true, presence: true,
if: :akismet_enabled if: :akismet_enabled
validates :koding_url,
presence: true,
if: :koding_enabled
validates :max_attachment_size, validates :max_attachment_size,
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
...@@ -157,6 +161,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -157,6 +161,8 @@ class ApplicationSetting < ActiveRecord::Base
two_factor_grace_period: 48, two_factor_grace_period: 48,
recaptcha_enabled: false, recaptcha_enabled: false,
akismet_enabled: false, akismet_enabled: false,
koding_enabled: false,
koding_url: nil,
repository_checks_enabled: true, repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [], disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false, send_user_confirmation_email: false,
......
...@@ -62,6 +62,7 @@ module Ci ...@@ -62,6 +62,7 @@ module Ci
status_event: 'enqueue' status_event: 'enqueue'
) )
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build) MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
build.pipeline.mark_as_processable_after_stage(build.stage_idx)
new_build new_build
end end
end end
...@@ -344,7 +345,7 @@ module Ci ...@@ -344,7 +345,7 @@ module Ci
def execute_hooks def execute_hooks
return unless project return unless project
build_data = Gitlab::BuildDataBuilder.build(self) build_data = Gitlab::DataBuilder::Build.build(self)
project.execute_hooks(build_data.dup, :build_hooks) project.execute_hooks(build_data.dup, :build_hooks)
project.execute_services(build_data.dup, :build_hooks) project.execute_services(build_data.dup, :build_hooks)
PagesService.new(build_data).execute PagesService.new(build_data).execute
......
...@@ -19,6 +19,8 @@ module Ci ...@@ -19,6 +19,8 @@ module Ci
after_save :keep_around_commits after_save :keep_around_commits
delegate :stages, to: :statuses
state_machine :status, initial: :created do state_machine :status, initial: :created do
event :enqueue do event :enqueue do
transition created: :pending transition created: :pending
...@@ -56,6 +58,10 @@ module Ci ...@@ -56,6 +58,10 @@ module Ci
before_transition do |pipeline| before_transition do |pipeline|
pipeline.update_duration pipeline.update_duration
end end
after_transition do |pipeline, transition|
pipeline.execute_hooks unless transition.loopback?
end
end end
# ref can't be HEAD or SHA, can only be branch/tag name # ref can't be HEAD or SHA, can only be branch/tag name
...@@ -72,6 +78,10 @@ module Ci ...@@ -72,6 +78,10 @@ module Ci
CommitStatus.where(pipeline: pluck(:id)).stages CommitStatus.where(pipeline: pluck(:id)).stages
end end
def self.total_duration
where.not(duration: nil).sum(:duration)
end
def stages_with_latest_statuses def stages_with_latest_statuses
statuses.latest.order(:stage_idx).group_by(&:stage) statuses.latest.order(:stage_idx).group_by(&:stage)
end end
...@@ -140,6 +150,10 @@ module Ci ...@@ -140,6 +150,10 @@ module Ci
end end
end end
def mark_as_processable_after_stage(stage_idx)
builds.skipped.where('stage_idx > ?', stage_idx).find_each(&:process)
end
def latest? def latest?
return false unless ref return false unless ref
commit = project.commit(ref) commit = project.commit(ref)
...@@ -244,11 +258,21 @@ module Ci ...@@ -244,11 +258,21 @@ module Ci
end end
def update_duration def update_duration
self.duration = statuses.latest.duration self.duration = calculate_duration
end
def execute_hooks
data = pipeline_data
project.execute_hooks(data, :pipeline_hooks)
project.execute_services(data, :pipeline_hooks)
end end
private private
def pipeline_data
Gitlab::DataBuilder::Pipeline.build(self)
end
def latest_builds_status def latest_builds_status
return 'failed' unless yaml_errors.blank? return 'failed' unless yaml_errors.blank?
......
...@@ -229,7 +229,7 @@ class Commit ...@@ -229,7 +229,7 @@ class Commit
def diff_refs def diff_refs
Gitlab::Diff::DiffRefs.new( Gitlab::Diff::DiffRefs.new(
base_sha: self.parent_id || self.sha, base_sha: self.parent_id || Gitlab::Git::BLANK_SHA,
head_sha: self.sha head_sha: self.sha
) )
end end
......
...@@ -21,6 +21,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -21,6 +21,7 @@ class CommitStatus < ActiveRecord::Base
where(id: max_id.group(:name, :commit_id)) where(id: max_id.group(:name, :commit_id))
end end
scope :retried, -> { where.not(id: latest) } scope :retried, -> { where.not(id: latest) }
scope :ordered, -> { order(:name) } scope :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
...@@ -30,6 +31,10 @@ class CommitStatus < ActiveRecord::Base ...@@ -30,6 +31,10 @@ class CommitStatus < ActiveRecord::Base
transition [:created, :skipped] => :pending transition [:created, :skipped] => :pending
end end
event :process do
transition skipped: :created
end
event :run do event :run do
transition pending: :running transition pending: :running
end end
...@@ -107,13 +112,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -107,13 +112,7 @@ class CommitStatus < ActiveRecord::Base
end end
def duration def duration
duration = calculate_duration
if started_at && finished_at
finished_at - started_at
elsif started_at
Time.now - started_at
end
duration
end end
def stuck? def stuck?
......
module Expirable
extend ActiveSupport::Concern
included do
scope :expired, -> { where('expires_at <= ?', Time.current) }
end
def expires?
expires_at.present?
end
def expires_soon?
expires_at < 7.days.from_now
end
end
...@@ -17,6 +17,10 @@ module NoteOnDiff ...@@ -17,6 +17,10 @@ module NoteOnDiff
raise NotImplementedError raise NotImplementedError
end end
def original_line_code
raise NotImplementedError
end
def diff_attributes def diff_attributes
raise NotImplementedError raise NotImplementedError
end end
......
...@@ -35,11 +35,6 @@ module Statuseable ...@@ -35,11 +35,6 @@ module Statuseable
all.pluck(self.status_sql).first all.pluck(self.status_sql).first
end end
def duration
duration_array = all.map(&:duration).compact
duration_array.reduce(:+)
end
def started_at def started_at
all.minimum(:started_at) all.minimum(:started_at)
end end
...@@ -85,4 +80,14 @@ module Statuseable ...@@ -85,4 +80,14 @@ module Statuseable
def complete? def complete?
COMPLETED_STATUSES.include?(status) COMPLETED_STATUSES.include?(status)
end end
private
def calculate_duration
if started_at && finished_at
finished_at - started_at
elsif started_at
Time.now - started_at
end
end
end end
...@@ -57,6 +57,10 @@ class DiffNote < Note ...@@ -57,6 +57,10 @@ class DiffNote < Note
diff_file.position(line) == self.original_position diff_file.position(line) == self.original_position
end end
def original_line_code
self.diff_file.line_code(self.diff_line)
end
def active?(diff_refs = nil) def active?(diff_refs = nil)
return false unless supported? return false unless supported?
return true if for_commit? return true if for_commit?
......
...@@ -12,6 +12,7 @@ class Discussion ...@@ -12,6 +12,7 @@ class Discussion
:for_merge_request?, :for_merge_request?,
:line_code, :line_code,
:original_line_code,
:diff_file, :diff_file,
:for_line?, :for_line?,
:active?, :active?,
......
...@@ -107,34 +107,41 @@ class Group < Namespace ...@@ -107,34 +107,41 @@ class Group < Namespace
end end
end end
def add_users(user_ids, access_level, current_user = nil, skip_notification: false) def add_users(user_ids, access_level, current_user: nil, skip_notification: false, expires_at: nil)
user_ids.each do |user_id| user_ids.each do |user_id|
Member.add_user(self.group_members, user_id, access_level, current_user, skip_notification: skip_notification) Member.add_user(
self.group_members,
user_id,
access_level,
current_user: current_user,
skip_notification: skip_notification,
expires_at: expires_at
)
end end
end end
def add_user(user, access_level, current_user = nil, skip_notification: false) def add_user(user, access_level, current_user: nil, skip_notification: false, expires_at: nil)
add_users([user], access_level, current_user, skip_notification: skip_notification) add_users([user], access_level, current_user: current_user, skip_notification: skip_notification, expires_at: expires_at)
end end
def add_owner(user, current_user = nil, skip_notification: false) def add_owner(user, current_user = nil, skip_notification: false)
self.add_user(user, Gitlab::Access::OWNER, current_user, skip_notification: skip_notification) add_user(user, Gitlab::Access::OWNER, current_user: current_user, skip_notification: skip_notification)
end end
def add_guest(user, current_user = nil) def add_guest(user, current_user = nil)
add_user(user, Gitlab::Access::GUEST, current_user) add_user(user, Gitlab::Access::GUEST, current_user: current_user)
end end
def add_reporter(user, current_user = nil) def add_reporter(user, current_user = nil)
add_user(user, Gitlab::Access::REPORTER, current_user) add_user(user, Gitlab::Access::REPORTER, current_user: current_user)
end end
def add_developer(user, current_user = nil) def add_developer(user, current_user = nil)
add_user(user, Gitlab::Access::DEVELOPER, current_user) add_user(user, Gitlab::Access::DEVELOPER, current_user: current_user)
end end
def add_master(user, current_user = nil) def add_master(user, current_user = nil)
add_user(user, Gitlab::Access::MASTER, current_user) add_user(user, Gitlab::Access::MASTER, current_user: current_user)
end end
def has_owner?(user) def has_owner?(user)
......
...@@ -9,5 +9,6 @@ class ProjectHook < WebHook ...@@ -9,5 +9,6 @@ class ProjectHook < WebHook
scope :note_hooks, -> { where(note_events: true) } scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :build_hooks, -> { where(build_events: true) } scope :build_hooks, -> { where(build_events: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
end end
...@@ -8,6 +8,7 @@ class WebHook < ActiveRecord::Base ...@@ -8,6 +8,7 @@ class WebHook < ActiveRecord::Base
default_value_for :merge_requests_events, false default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false default_value_for :tag_push_events, false
default_value_for :build_events, false default_value_for :build_events, false
default_value_for :pipeline_events, false
default_value_for :enable_ssl_verification, true default_value_for :enable_ssl_verification, true
scope :push_hooks, -> { where(push_events: true) } scope :push_hooks, -> { where(push_events: true) }
......
...@@ -49,6 +49,10 @@ class LegacyDiffNote < Note ...@@ -49,6 +49,10 @@ class LegacyDiffNote < Note
!line.meta? && diff_file.line_code(line) == self.line_code !line.meta? && diff_file.line_code(line) == self.line_code
end end
def original_line_code
self.line_code
end
# Check if this note is part of an "active" discussion # Check if this note is part of an "active" discussion
# #
# This will always return true for anything except MergeRequest noteables, # This will always return true for anything except MergeRequest noteables,
......
class Member < ActiveRecord::Base class Member < ActiveRecord::Base
include Sortable include Sortable
include Importable include Importable
include Expirable
include Gitlab::Access include Gitlab::Access
attr_accessor :raw_invite_token attr_accessor :raw_invite_token
...@@ -74,7 +75,7 @@ class Member < ActiveRecord::Base ...@@ -74,7 +75,7 @@ class Member < ActiveRecord::Base
user user
end end
def add_user(members, user_id, access_level, current_user = nil, skip_notification: false) def add_user(members, user_id, access_level, current_user: nil, skip_notification: false, expires_at: nil)
user = user_for_id(user_id) user = user_for_id(user_id)
# `user` can be either a User object or an email to be invited # `user` can be either a User object or an email to be invited
...@@ -88,7 +89,7 @@ class Member < ActiveRecord::Base ...@@ -88,7 +89,7 @@ class Member < ActiveRecord::Base
if can_update_member?(current_user, member) || project_creator?(member, access_level) if can_update_member?(current_user, member) || project_creator?(member, access_level)
member.created_by ||= current_user member.created_by ||= current_user
member.access_level = access_level member.access_level = access_level
member.expires_at = expires_at
member.skip_notification = skip_notification member.skip_notification = skip_notification
member.save member.save
......
...@@ -34,7 +34,7 @@ class ProjectMember < Member ...@@ -34,7 +34,7 @@ class ProjectMember < Member
# :master # :master
# ) # )
# #
def add_users_to_projects(project_ids, user_ids, access, current_user = nil) def add_users_to_projects(project_ids, user_ids, access, current_user: nil, expires_at: nil)
access_level = if roles_hash.has_key?(access) access_level = if roles_hash.has_key?(access)
roles_hash[access] roles_hash[access]
elsif roles_hash.values.include?(access.to_i) elsif roles_hash.values.include?(access.to_i)
...@@ -50,7 +50,13 @@ class ProjectMember < Member ...@@ -50,7 +50,13 @@ class ProjectMember < Member
project = Project.find(project_id) project = Project.find(project_id)
users.each do |user| users.each do |user|
Member.add_user(project.project_members, user, access_level, current_user) Member.add_user(
project.project_members,
user,
access_level,
current_user: current_user,
expires_at: expires_at
)
end end
end end
end end
......
...@@ -1110,8 +1110,8 @@ class Project < ActiveRecord::Base ...@@ -1110,8 +1110,8 @@ class Project < ActiveRecord::Base
project_members.find_by(user_id: user) project_members.find_by(user_id: user)
end end
def add_user(user, access_level, current_user = nil) def add_user(user, access_level, current_user: nil, expires_at: nil)
team.add_user(user, access_level, current_user) team.add_user(user, access_level, current_user: current_user, expires_at: expires_at)
end end
def default_branch def default_branch
......
class ProjectGroupLink < ActiveRecord::Base class ProjectGroupLink < ActiveRecord::Base
include Expirable
GUEST = 10 GUEST = 10
REPORTER = 20 REPORTER = 20
DEVELOPER = 30 DEVELOPER = 30
......
...@@ -51,8 +51,7 @@ class BuildsEmailService < Service ...@@ -51,8 +51,7 @@ class BuildsEmailService < Service
end end
def test_data(project = nil, user = nil) def test_data(project = nil, user = nil)
build = project.builds.last Gitlab::DataBuilder::Build.build(project.builds.last)
Gitlab::BuildDataBuilder.build(build)
end end
def fields def fields
......
...@@ -15,9 +15,9 @@ class ProjectTeam ...@@ -15,9 +15,9 @@ class ProjectTeam
users, access, current_user = *args users, access, current_user = *args
if users.respond_to?(:each) if users.respond_to?(:each)
add_users(users, access, current_user) add_users(users, access, current_user: current_user)
else else
add_user(users, access, current_user) add_user(users, access, current_user: current_user)
end end
end end
...@@ -33,19 +33,20 @@ class ProjectTeam ...@@ -33,19 +33,20 @@ class ProjectTeam
member member
end end
def add_users(users, access, current_user = nil) def add_users(users, access, current_user: nil, expires_at: nil)
return false if group_member_lock return false if group_member_lock
ProjectMember.add_users_to_projects( ProjectMember.add_users_to_projects(
[project.id], [project.id],
users, users,
access, access,
current_user current_user: current_user,
expires_at: expires_at
) )
end end
def add_user(user, access, current_user = nil) def add_user(user, access, current_user: nil, expires_at: nil)
add_users([user], access, current_user) add_users([user], access, current_user: current_user, expires_at: expires_at)
end end
# Remove all users from project team # Remove all users from project team
......
...@@ -342,7 +342,7 @@ class Repository ...@@ -342,7 +342,7 @@ class Repository
def cache_keys def cache_keys
%i(size commit_count %i(size commit_count
readme version contribution_guide changelog readme version contribution_guide changelog
license_blob license_key gitignore) license_blob license_key gitignore koding_yml)
end end
# Keys for data on branch/tag operations. # Keys for data on branch/tag operations.
...@@ -618,6 +618,14 @@ class Repository ...@@ -618,6 +618,14 @@ class Repository
end end
end end
def koding_yml
return nil unless head_exists?
cache.fetch(:koding_yml) do
file_on_head(/\A\.koding\.yml\z/)
end
end
def gitlab_ci_yml def gitlab_ci_yml
return nil unless head_exists? return nil unless head_exists?
......
...@@ -36,6 +36,7 @@ class Service < ActiveRecord::Base ...@@ -36,6 +36,7 @@ class Service < ActiveRecord::Base
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
scope :note_hooks, -> { where(note_events: true, active: true) } scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) } scope :build_hooks, -> { where(build_events: true, active: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true, active: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
scope :external_issue_trackers, -> { issue_trackers.active.without_defaults } scope :external_issue_trackers, -> { issue_trackers.active.without_defaults }
...@@ -79,13 +80,17 @@ class Service < ActiveRecord::Base ...@@ -79,13 +80,17 @@ class Service < ActiveRecord::Base
end end
def test_data(project, user) def test_data(project, user)
Gitlab::PushDataBuilder.build_sample(project, user) Gitlab::DataBuilder::Push.build_sample(project, user)
end end
def event_channel_names def event_channel_names
[] []
end end
def event_names
supported_events.map { |event| "#{event}_events" }
end
def event_field(event) def event_field(event)
nil nil
end end
......
...@@ -39,7 +39,12 @@ class DeleteBranchService < BaseService ...@@ -39,7 +39,12 @@ class DeleteBranchService < BaseService
end end
def build_push_data(branch) def build_push_data(branch)
Gitlab::PushDataBuilder Gitlab::DataBuilder::Push.build(
.build(project, current_user, branch.target.sha, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) project,
current_user,
branch.target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}",
[])
end end
end end
...@@ -33,7 +33,12 @@ class DeleteTagService < BaseService ...@@ -33,7 +33,12 @@ class DeleteTagService < BaseService
end end
def build_push_data(tag) def build_push_data(tag)
Gitlab::PushDataBuilder Gitlab::DataBuilder::Push.build(
.build(project, current_user, tag.target.sha, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", []) project,
current_user,
tag.target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
[])
end end
end end
...@@ -143,13 +143,23 @@ class GitPushService < BaseService ...@@ -143,13 +143,23 @@ class GitPushService < BaseService
end end
def build_push_data def build_push_data
@push_data ||= Gitlab::PushDataBuilder. @push_data ||= Gitlab::DataBuilder::Push.build(
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits) @project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
push_commits)
end end
def build_push_data_system_hook def build_push_data_system_hook
@push_data_system ||= Gitlab::PushDataBuilder. @push_data_system ||= Gitlab::DataBuilder::Push.build(
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], []) @project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
[])
end end
def push_to_existing_branch? def push_to_existing_branch?
......
...@@ -34,12 +34,24 @@ class GitTagPushService < BaseService ...@@ -34,12 +34,24 @@ class GitTagPushService < BaseService
end end
end end
Gitlab::PushDataBuilder. Gitlab::DataBuilder::Push.build(
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], commits, message) project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
commits,
message)
end end
def build_system_push_data def build_system_push_data
Gitlab::PushDataBuilder. Gitlab::DataBuilder::Push.build(
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], [], '') project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
[],
'')
end end
end end
module Members
class AuthorizedDestroyService < BaseService
attr_accessor :member, :user
def initialize(member, user = nil)
@member, @user = member, user
end
def execute
return false if member.is_a?(GroupMember) && member.source.last_owner?(member.user)
member.destroy
if member.request? && member.user != user
notification_service.decline_access_request(member)
end
end
end
end
...@@ -11,12 +11,7 @@ module Members ...@@ -11,12 +11,7 @@ module Members
unless member && can?(current_user, "destroy_#{member.type.underscore}".to_sym, member) unless member && can?(current_user, "destroy_#{member.type.underscore}".to_sym, member)
raise Gitlab::Access::AccessDeniedError raise Gitlab::Access::AccessDeniedError
end end
AuthorizedDestroyService.new(member, current_user).execute
member.destroy
if member.request? && member.user != current_user
notification_service.decline_access_request(member)
end
end end
end end
end end
...@@ -16,7 +16,7 @@ module Notes ...@@ -16,7 +16,7 @@ module Notes
end end
def hook_data def hook_data
Gitlab::NoteDataBuilder.build(@note, @note.author) Gitlab::DataBuilder::Note.build(@note, @note.author)
end end
def execute_note_hooks def execute_note_hooks
......
...@@ -255,7 +255,6 @@ class NotificationService ...@@ -255,7 +255,6 @@ class NotificationService
project_member.real_source_type, project_member.real_source_type,
project_member.project.id, project_member.project.id,
project_member.invite_email, project_member.invite_email,
project_member.access_level,
project_member.created_by_id project_member.created_by_id
).deliver_later ).deliver_later
end end
...@@ -282,7 +281,6 @@ class NotificationService ...@@ -282,7 +281,6 @@ class NotificationService
group_member.real_source_type, group_member.real_source_type,
group_member.group.id, group_member.group.id,
group_member.invite_email, group_member.invite_email,
group_member.access_level,
group_member.created_by_id group_member.created_by_id
).deliver_later ).deliver_later
end end
......
class TestHookService class TestHookService
def execute(hook, current_user) def execute(hook, current_user)
data = Gitlab::PushDataBuilder.build_sample(project(hook), current_user) data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user)
hook.execute(data, 'push_hooks') hook.execute(data, 'push_hooks')
end end
......
...@@ -439,6 +439,25 @@ ...@@ -439,6 +439,25 @@
.col-sm-10 .col-sm-10
= f.text_field :elasticsearch_port, class: 'form-control', placeholder: ApplicationSetting.current.elasticsearch_port = f.text_field :elasticsearch_port, class: 'form-control', placeholder: ApplicationSetting.current.elasticsearch_port
%fieldset
%legend Koding
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :koding_enabled do
= f.check_box :koding_enabled
Enable Koding
.form-group
= f.label :koding_url, 'Koding URL', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :koding_url, class: 'form-control', placeholder: 'http://gitlab.your-koding-instance.com:8090'
.help-block
Koding has integration enabled out of the box for the
%strong gitlab
team, and you need to provide that team's URL here. Learn more in the
= succeed "." do
= link_to "Koding integration documentation", help_page_path("integration/koding")
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save'
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
- if build.duration - if build.duration
%p.duration %p.duration
= custom_icon("icon_timer") = custom_icon("icon_timer")
= duration_in_numbers(build.finished_at, build.started_at) = duration_in_numbers(build.duration)
- if build.finished_at - if build.finished_at
%p.finished-at %p.finished-at
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.diff-content.code.js-syntax-highlight .diff-content.code.js-syntax-highlight
%table %table
- discussions = { discussion.line_code => discussion } - discussions = { discussion.original_line_code => discussion }
= render partial: "projects/diffs/line", = render partial: "projects/diffs/line",
collection: discussion.truncated_diff_lines, collection: discussion.truncated_diff_lines,
as: :line, as: :line,
......
...@@ -14,5 +14,14 @@ ...@@ -14,5 +14,14 @@
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("user/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.form-group
= f.label :expires_at, 'Access expiration date', class: 'control-label'
.col-sm-10
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
%i.clear-icon.js-clear-input
.help-block
On this date, the user(s) will automatically lose access to this group and all of its projects.
.form-actions .form-actions
= f.submit 'Add users to group', class: "btn btn-create" = f.submit 'Add users to group', class: "btn btn-create"
:plain :plain
$("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}');
new MemberExpirationDate();
.row-content-block.second-block.center
%p
= icon('circle', class: 'cgreen')
Integration is active for
= link_to koding_project_url, target: '_blank' do
#{current_application_settings.koding_url}
.documentation.wiki
= markdown @markdown
- page_title "Koding"
- page_description "Koding Dashboard"
- header_title "Koding", koding_path
= render template: "layouts/application"
...@@ -12,6 +12,11 @@ ...@@ -12,6 +12,11 @@
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
%span %span
Activity Activity
- if koding_enabled?
= nav_link(controller: :koding) do
= link_to koding_path, title: 'Koding' do
%span
Koding
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do = link_to dashboard_groups_path, title: 'Groups' do
%span %span
......
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
Graphs Graphs
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: [:issues, :labels, :milestones]) do = nav_link(controller: [:issues, :labels, :milestones, :boards]) do
= link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do
%span %span
Issues Issues
......
- page_title "Edit", @blob.path, @ref - page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_tag('blob_edit/blob_edit_bundle.js')
- if @conflict - if @conflict
.alert.alert-danger .alert.alert-danger
...@@ -16,14 +19,10 @@ ...@@ -16,14 +19,10 @@
= link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do
= editing_preview_title(@blob.name) = editing_preview_title(@blob.name)
= form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form') do = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}" = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit_sha', @last_commit_sha = hidden_field_tag 'last_commit_sha', @last_commit_sha
= hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
= render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id) = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
:javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
new NewCommitForm($('.js-edit-blob-form'))
- page_title "New File", @path.presence, @ref - page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_tag('blob_edit/blob_edit_bundle.js')
%h3.page-title %h3.page-title
New File New File
.file-editor .file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-quick-submit js-requires-input') do = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths) do
= render 'projects/blob/editor', ref: @ref = render 'projects/blob/editor', ref: @ref
= render 'shared/new_commit_form', placeholder: "Add new file" = render 'shared/new_commit_form', placeholder: "Add new file"
= hidden_field_tag 'content', '', id: 'file-content' = hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref, = render 'projects/commit_button', ref: @ref,
cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) cancel_path: namespace_project_tree_path(@project.namespace, @project, @id)
:javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}")
new NewCommitForm($('.js-new-blob-form'))
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
.board-inner .board-inner
%header.board-header{ ":class" => "{ 'has-border': list.label }", ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" } %header.board-header{ ":class" => "{ 'has-border': list.label }", ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" }
%h3.board-title.js-board-handle{ ":class" => "{ 'user-can-drag': (!disabled && !list.preset) }" } %h3.board-title.js-board-handle{ ":class" => "{ 'user-can-drag': (!disabled && !list.preset) }" }
= icon("align-justify", class: "board-mobile-handle js-board-drag-handle", "v-if" => "(!disabled && !list.preset)")
{{ list.title }} {{ list.title }}
%span.pull-right{ "v-if" => "list.type !== 'blank'" } %span.pull-right{ "v-if" => "list.type !== 'blank'" }
{{ list.issues.length }} {{ list.issues.length }}
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
"track-by" => "id" } "track-by" => "id" }
%li.card{ ":class" => "{ 'user-can-drag': !disabled }", %li.card{ ":class" => "{ 'user-can-drag': !disabled }",
":index" => "index" } ":index" => "index" }
= icon("align-justify", class: "board-mobile-handle js-card-drag-handle", "v-if" => "!disabled")
%h4.card-title %h4.card-title
= icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential") = icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential")
%a{ ":href" => "issueLinkBase + '/' + issue.id", %a{ ":href" => "issueLinkBase + '/' + issue.id",
......
- if koding_enabled? && current_user && can_push_branch?(@project, @project.default_branch)
- if @repository.koding_yml
= link_to koding_project_url(@project), class: 'btn', target: '_blank' do
Run in IDE (Koding)
- else
= link_to add_koding_stack_path(@project), class: 'btn' do
Set Up Koding
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
- if build.duration - if build.duration
%p.duration %p.duration
= custom_icon("icon_timer") = custom_icon("icon_timer")
= duration_in_numbers(build.finished_at, build.started_at) = duration_in_numbers(build.duration)
- if build.finished_at - if build.finished_at
%p.finished-at %p.finished-at
= icon("calendar") = icon("calendar")
......
...@@ -48,10 +48,10 @@ ...@@ -48,10 +48,10 @@
\- \-
%td %td
- if pipeline.started_at && pipeline.finished_at - if pipeline.duration
%p.duration %p.duration
= custom_icon("icon_timer") = custom_icon("icon_timer")
= duration_in_numbers(pipeline.finished_at, pipeline.started_at) = duration_in_numbers(pipeline.duration)
- if pipeline.finished_at - if pipeline.finished_at
%p.finished-at %p.finished-at
= icon("calendar") = icon("calendar")
......
...@@ -56,10 +56,10 @@ ...@@ -56,10 +56,10 @@
= pluralize(@commit.pipelines.count, 'pipeline') = pluralize(@commit.pipelines.count, 'pipeline')
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do
= ci_icon_for_status(@commit.status) = ci_icon_for_status(@commit.status)
%span.ci-status-label
= ci_label_for_status(@commit.status) = ci_label_for_status(@commit.status)
- if @commit.pipelines.duration
in in
= time_interval_in_words @commit.pipelines.duration = time_interval_in_words @commit.pipelines.total_duration
.commit-box.content-block .commit-box.content-block
%h3.commit-title %h3.commit-title
......
...@@ -17,6 +17,13 @@ ...@@ -17,6 +17,13 @@
.select-wrapper .select-wrapper
= select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control" = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
%span.caret %span.caret
.form-group
= label_tag :expires_at, 'Access expiration date', class: 'label-light'
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
%i.clear-icon.js-clear-input
.help-block
On this date, all users in the group will automatically lose access to this project.
= submit_tag "Share", class: "btn btn-create" = submit_tag "Share", class: "btn btn-create"
.col-lg-9.col-lg-offset-3 .col-lg-9.col-lg-offset-3
%hr %hr
...@@ -35,6 +42,10 @@ ...@@ -35,6 +42,10 @@
= group.name = group.name
%br %br
up to #{group_link.human_access} up to #{group_link.human_access}
- if group_link.expires?
·
%span{ class: ('text-warning' if group_link.expires_soon?) }
expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
.pull-right .pull-right
= link_to namespace_project_group_link_path(@project.namespace, @project, group_link), method: :delete, class: "btn btn-transparent" do = link_to namespace_project_group_link_path(@project.namespace, @project, group_link), method: :delete, class: "btn btn-transparent" do
%span.sr-only disable sharing %span.sr-only disable sharing
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.col-md-8.col-lg-7 .col-md-8.col-lg-7
%strong.light-header= hook.url %strong.light-header= hook.url
%div %div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events wiki_page_events).each do |trigger| - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events pipeline_events wiki_page_events).each do |trigger|
- if hook.send(trigger) - if hook.send(trigger)
%span.label.label-gray.deploy-project-label= trigger.titleize %span.label.label-gray.deploy-project-label= trigger.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5 .col-md-4.col-lg-5.text-right-lg.prepend-top-5
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
- if @merge_request.open? - if @merge_request.open?
.pull-right .pull-right
- if @merge_request.source_branch_exists? - if @merge_request.source_branch_exists?
- if koding_enabled? && @repository.koding_yml
= link_to koding_project_url(@merge_request.source_project, @merge_request.source_branch, @merge_request.commits.first.short_id), class: "btn inline btn-grouped btn-sm", target: '_blank' do
Run in IDE (Koding)
= link_to "#modal_merge_info", class: "btn inline btn-grouped btn-sm", "data-toggle" => "modal" do = link_to "#modal_merge_info", class: "btn inline btn-grouped btn-sm", "data-toggle" => "modal" do
Check out branch Check out branch
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= link_to @pipeline.ref, namespace_project_commits_path(@project.namespace, @project, @pipeline.ref), class: "monospace" = link_to @pipeline.ref, namespace_project_commits_path(@project.namespace, @project, @pipeline.ref), class: "monospace"
- if @pipeline.duration - if @pipeline.duration
in in
= time_interval_in_words @pipeline.duration = time_interval_in_words(@pipeline.duration)
.pull-right .pull-right
= link_to namespace_project_pipeline_path(@project.namespace, @project, @pipeline), class: "ci-status ci-#{@pipeline.status}" do = link_to namespace_project_pipeline_path(@project.namespace, @project, @pipeline), class: "ci-status ci-#{@pipeline.status}" do
......
...@@ -14,5 +14,14 @@ ...@@ -14,5 +14,14 @@
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("user/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.form-group
= f.label :expires_at, 'Access expiration date', class: 'control-label'
.col-sm-10
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
%i.clear-icon.js-clear-input
.help-block
On this date, the user(s) will automatically lose access to this project.
.form-actions .form-actions
= f.submit 'Add users to project', class: "btn btn-create" = f.submit 'Add users to project', class: "btn btn-create"
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.alert.alert-warning .alert.alert-warning
Adding new users is disabled at group level Adding new users is disabled at group level
.project-members-page.prepend-top-default .project-members-page.js-project-members-page.prepend-top-default
- if can?(current_user, :admin_project_member, @project) && !membership_locked? - if can?(current_user, :admin_project_member, @project) && !membership_locked?
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
......
:plain :plain
$("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}'); $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}');
new MemberExpirationDate();
- @no_container = true
- page_title "Edit", @tag.name, "Tags" - page_title "Edit", @tag.name, "Tags"
= render "projects/commits/head" = render "projects/commits/head"
.row-content-block %div{ class: container_class }
.sub-header-block.no-bottom-space
.oneline .oneline
.title .title
Release notes for tag Release notes for tag
%strong #{@tag.name} %strong #{@tag.name}
.prepend-top-default
= form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f| = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f|
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..." = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..."
= render 'projects/notes/hints' = render 'projects/notes/hints'
.error-alert .error-alert
.form-actions.prepend-top-default .prepend-top-default
= f.submit 'Save changes', class: 'btn btn-save' = f.submit 'Save changes', class: 'btn btn-save'
= link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel"
...@@ -64,10 +64,12 @@ ...@@ -64,10 +64,12 @@
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
Set Up CI Set Up CI
%li.project-repo-buttons-right %li.project-repo-buttons-right
.project-repo-buttons.project-right-buttons .project-repo-buttons.project-right-buttons
- if current_user - if current_user
= render 'shared/members/access_request_buttons', source: @project = render 'shared/members/access_request_buttons', source: @project
= render "projects/buttons/koding"
.btn-group.project-repo-btn-group .btn-group.project-repo-btn-group
= render "projects/buttons/download" = render "projects/buttons/download"
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11"><path fill-rule="evenodd" d="m9.283 6.47l-7.564 4.254c-.949.534-1.719.266-1.719-.576v-9.292c0-.852.756-1.117 1.719-.576l7.564 4.254c.949.534.963 1.392 0 1.934"/></svg>
\ No newline at end of file
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
- if issuable.project.labels.any? - if issuable.project.labels.any?
.block.labels .block.labels
.sidebar-collapsed-icon .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } }
= icon('tags') = icon('tags')
%span %span
= issuable.labels_array.size = issuable.labels_array.size
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= button_tag icon('pencil'), = button_tag icon('pencil'),
type: 'button', type: 'button',
class: 'btn inline js-toggle-button', class: 'btn inline js-toggle-button',
title: 'Edit access level' title: 'Edit'
- if member.request? - if member.request?
= link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]), = link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
...@@ -59,6 +59,10 @@ ...@@ -59,6 +59,10 @@
= time_ago_with_tooltip(member.requested_at) = time_ago_with_tooltip(member.requested_at)
- else - else
Joined #{time_ago_with_tooltip(member.created_at)} Joined #{time_ago_with_tooltip(member.created_at)}
- if member.expires?
·
%span{ class: ('text-warning' if member.expires_soon?) }
Expires in #{distance_of_time_in_words_to_now(member.expires_at)}
- else - else
= image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: '' = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: ''
...@@ -73,8 +77,16 @@ ...@@ -73,8 +77,16 @@
- if show_roles - if show_roles
.edit-member.hide.js-toggle-content .edit-member.hide.js-toggle-content
%br %br
= form_for member, remote: true do |f| = form_for member, remote: true, html: { class: 'form-horizontal' } do |f|
.prepend-top-10 .form-group
= f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control' = label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label'
.col-sm-10
= f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}"
.form-group
= label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label'
.col-sm-10
.clearable-input
= f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}"
%i.clear-icon.js-clear-input
.prepend-top-10 .prepend-top-10
= f.submit 'Save', class: 'btn btn-save btn-sm' = f.submit 'Save', class: 'btn btn-save btn-sm'
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_tag('snippet/snippet_bundle.js')
.snippet-form-holder .snippet-form-holder
= form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f| = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f|
= form_errors(@snippet) = form_errors(@snippet)
...@@ -31,8 +35,3 @@ ...@@ -31,8 +35,3 @@
- else - else
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel" = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
:javascript
var editor = ace.edit("editor");
$(".snippet-form-holder form").submit(function(){
$(".snippet-file-content").val(editor.getValue());
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment