Merge branch 'master' into 'boards-bundle-refactor'

# Conflicts:
#   config/webpack.config.js
This commit is contained in:
Jacob Schatz 2018-02-23 20:36:28 +00:00
commit a90a22a774
550 changed files with 7467 additions and 3230 deletions

View File

@ -2,6 +2,202 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 10.5.1 (2018-02-22)
- No changes.
## 10.5.0 (2018-02-22)
### Security (3 changes, 1 of them is from the community)
- Update marked from 0.3.6 to 0.3.12. !16480 (Takuya Noguchi)
- Update nokogiri to 1.8.2. !16807
- Add verification for GitLab Pages custom domains.
### Fixed (77 changes, 25 of them are from the community)
- Fix the Projects API with_issues_enabled filter behaving incorrectly any user. !12724 (Jan Christophersen)
- Hide pipeline schedule take ownership for current owner. !12986
- Handle special characters on API request of issuable templates. !15323 (Takuya Noguchi)
- Shows signin tab after new user email confirmation. !16174 (Jacopo Beschi @jacopo-beschi)
- Make project README containers wider on fixed layout. !16181 (Takuya Noguchi)
- Fix dashboard projects nav links height. !16204 (George Tsiolis)
- Fix error on empty query for Members API. !16235
- Issue board: fix for dragging an issue to the very bottom in long lists. !16250 (David Kuri)
- Make rich blob viewer wider for PC. !16262 (Takuya Noguchi)
- Substitute deprecated ui_charcoal with new default ui_indigo. !16271 (Takuya Noguchi)
- Generate HTTP URLs for custom Pages domains when appropriate. !16279
- Make modal dialog common for Groups tree app. !16311
- Allow moving wiki pages from the UI. !16313
- Filter groups and projects dropdowns of search page on backend. !16336
- Adjust layout width for fixed layout. !16337 (George Tsiolis)
- Fix custom header logo design nitpick: Remove unneeded margin on empty logo text. !16383 (Markus Doits)
- File Upload UI can create LFS pointers based on .gitattributes. !16412
- Fix Ctrl+Enter keyboard shortcut saving comment/note edit. !16415
- Fix file search results when they match file contents with a number between two colons. !16462
- Fix tooltip displayed for running manual actions. !16489
- Allow trailing + on labels in board filters. !16490
- Prevent JIRA issue identifier from being humanized. !16491 (Andrew McCallum)
- Add horizontal scroll to wiki tables. !16527 (George Tsiolis)
- Fix a bug calculating artifact size for project statistics. !16539
- Stop loading spinner on error of issuable templates. !16600 (Takuya Noguchi)
- Allows html text in commits atom feed. !16603 (Jacopo Beschi @jacopo-beschi)
- Disable MR check out button when source branch is deleted. !16631 (Jacopo Beschi @jacopo-beschi)
- Fix export removal for hashed-storage projects within a renamed or deleted namespace. !16658
- Default to HTTPS for all Gravatar URLs. !16666
- Login via OAuth now only marks new users as external. !16672
- Fix default avatar icon missing when Gravatar is disabled. !16681 (Felix Geyer)
- Change button group width on mobile. !16726 (George Tsiolis)
- Fix version information not showing on help page if commercial content display was disabled. !16743
- Adds spacing between edit and delete tag btn in tag list. !16757 (Jacopo Beschi @jacopo-beschi)
- Fix 500 error when loading a merge request with an invalid comment. !16795
- Deleting an upload will correctly clean up the filesystem. !16799
- Cleanup new branch/merge request form in issues. !16854
- Fix GitLab import leaving group_id on ProjectLabel. !16877
- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881
- Trigger change event on filename input when file template is applied. !16911 (Sebastian Klingler)
- Fixes different margins between buttons in tag list. !16927 (Jacopo Beschi @jacopo-beschi)
- Close low level rugged repository in project cache worker. !16930 (Bastian Blank)
- Override group sidebar links. !16942 (George Tsiolis)
- Avoid running `PopulateForkNetworksRange`-migration multiple times. !16988
- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019
- Fix monaco editor features which were incompatable with GitLab CDN settings. !17021
- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054
- Fix cnacel edit note button reverting changes. !42462
- For issues display time of last edit of title or description instead of time of any attribute change.
- Handle all Psych YAML parser exceptions (fixes #41209).
- Fix validation of environment scope of variables.
- Display user friendly error message if rebase fails.
- Hide new branch and tag links for projects with an empty repo.
- Fix protected branches API to accept name parameter with dot.
- Closes #38540 - Remove .ssh/environment file that now breaks the gitlab:check rake task.
- Keep subscribers when promoting labels to group labels.
- Replace verified badge icons and uniform colors.
- Fix error on changes tab when merge request cannot be created.
- Ignore leading slashes when searching for files within context of repository. (Andrew McCallum)
- Close and do not reload MR diffs when source branch is deleted.
- Bypass commits title markdown on notes.
- Reload MRs memoization after diffs creation.
- Return more consistent values for merge_status on MR APIs.
- Contribution calendar label was cut off. (Branka Martinovic)
- LDAP Person no longer throws exception on invalid entry.
- Fix bug where award emojis would be lost when moving issues between projects.
- Fix not all events being shown in group dashboard.
- Fix JIRA not working when a trailing slash is included.
- Fix squash not working when diff contained non-ASCII data.
- Remove erroneous text in shared runners page that suggested more runners available.
- Execute system hooks after-commit when executing project hooks.
- Makes forking protect default branch on completion.
- Validate user, group and project paths consistently, and only once.
- Validate user namespace before saving so that errors persist on model.
- Permits 'password_authentication_enabled_for_git' parameter for ApplicationSettingsController.
- Fix duplicate item in protected branch/tag dropdown.
- Open visibility level help in a new tab. (Jussi Räsänen)
### Deprecated (1 change)
- Add note within ux documentation that further changes should be made within the design.gitlab project.
### Changed (20 changes, 7 of them are from the community)
- Show coverage to two decimal points in coverage badge. !10083 (Jeff Stubler)
- Update 'removed assignee' note to include old assignee reference. !16301 (Maurizio De Santis)
- Move row containing Projects, Users and Groups count to the top in admin dashboard. !16421
- Add Auto DevOps Domain application setting. !16604
- Changes Revert this merge request text. !16611 (Jacopo Beschi @jacopo-beschi)
- Link Auto DevOps settings to Clusters page. !16641
- Internationalize charts page. !16687 (selrahman)
- Internationalize graph page selrahman. !16688 (Shah El-Rahman)
- Save traces as artifacts. !16702
- Hide variable values on pipeline schedule edit page. !16729
- Update runner info on all authenticated requests. !16756
- Improve issue note dropdown and mr button. !16758 (George Tsiolis)
- Replace "cluster" with "Kubernetes cluster". !16778
- Enable Prometheus metrics for deployed Ingresses. !16866 (joshlambert)
- Rename button to enable CI/CD configuration to "Set up CI/CD". !16870
- Double padding for file-content wiki class on larger screens.
- Improve wording about additional costs for Ingress on custom clusters.
- Last push widget will show banner for new pushes to previously merged branch.
- Save user ID and username in Grape API log (api_json.log).
- Include subgroup issues and merge requests on the group page.
### Performance (14 changes, 1 of them is from the community)
- Fix double query execution on groups page. !16314
- Speed up loading merged merge requests when they contained a lot of commits before merging. !16320
- Properly memoize some predicate methods. !16329
- Reduce the number of Prometheus metrics. !16443
- Only highlight search results under the highlighting size limit. !16462
- Add fast-blank. !16468
- Move BoardList vue component to vue file. !16888 (George Tsiolis)
- Fix N+1 query problem for snippets dashboard. !16944
- Optimize search queries on the search page by setting a limit for matching records.
- Store number of commits in merge_request_diffs table.
- Improve performance of target branch dropdown.
- Remove duplicate calls of MergeRequest#can_be_reverted?.
- Stop checking if discussions are in a mergeable state if the MR isn't.
- Remove N+1 queries with /projects/:project_id/{access_requests,members} API endpoints.
### Added (28 changes, 10 of them are from the community)
- Add link on commit page to merge request that introduced that commit. !13713 (Hiroyuki Sato)
- System hooks for Merge Requests. !14387 (Alexis Reigel)
- Add `pipelines` endpoint to merge requests API. !15454 (Tony Rom <thetonyrom@gmail.com>)
- Adds Rubocop rule for line break around conditionals. !15739 (Jacopo Beschi @jacopo-beschi)
- Add Colors to GitLab Flavored Markdown. !16095 (Tony Rom <thetonyrom@gmail.com>)
- Initial work to add notification reason to emails. !16160 (Mario de la Ossa)
- Implement multi server support and use kube proxy to connect to Prometheus servers inside K8S cluster. !16182
- Add ability to transfer a group into another group. !16302
- Add blue dot feature highlight to make GKE Clusters more visible to users. !16379
- Add section headers to plus button dropdown. !16394 (George Tsiolis)
- Support PostgreSQL 10. !16471
- Enables Project Milestone Deletion via the API. !16478 (Jacopo Beschi @jacopo-beschi)
- Add realtime ci status for the repository -> files view. !16523
- User can now git push to create a new project. !16547
- Improve empty project overview. !16617 (George Tsiolis)
- Added uploader metadata to the uploads. !16779
- Added ldap config setting to lower case the username. !16791
- Add search support into the API. !16878
- Backport of LFS File Locking API. !16935
- Add a link to documentation on how to get external ip in the Kubernetes cluster details page. !16937
- Add sorting options for /users API (admin only). !16945
- Adds sorting to deployments API. (Jacopo Beschi @jacopo-beschi)
- Add rake task to check integrity of uploaded files.
- Add backend for persistently dismissably callouts.
- Track and act upon the number of executed queries.
- Add a gRPC health check to ensure Gitaly is up.
- Log and send a system hook if a blocked user attempts to login.
- Add Gitaly Servers admin dashboard.
### Other (25 changes, 7 of them are from the community)
- Updated the katex library. !15864
- Add modal for deleting a milestone. !16229
- Remove unused CSS selectors for Cycle Analytics. !16270 (Takuya Noguchi)
- Add reason to keep postgresql 9.2 for CI. !16277 (Takuya Noguchi)
- Adjust modal style to new design. !16310
- Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH pull. !16586
- Set timezone for karma to UTC. !16602 (Takuya Noguchi)
- Make Gitaly RepositoryExists opt-out. !16680
- Update minimum git version to 2.9.5. !16683
- Disable throwOnError in KaTeX to reveal user where is the problem. !16684 (Jakub Jirutka)
- fix documentation about node version. !16720 (Tobias Gurtzick)
- Enable RuboCop Style/RegexpLiteral. !16752 (Takuya Noguchi)
- Add confirmation-input component. !16816
- Add unique constraint to trending_projects#project_id. !16846
- Add foreign key and NOT NULL constraints to todos table. !16849
- Include branch in mobile view for pipelines. !16910 (George Tsiolis)
- Downgrade google-protobuf gem. !16941
- Refactors mr widget components into vue files and adds i18n.
- increase-readability-of-colored-text-in-job-output-log.
- Finish any remaining jobs for issues.closed_at.
- Translate issuable sidebar.
- Set standard disabled state for all buttons.
- Upgrade GitLab Workhorse to v3.6.0.
- Improve readability of underlined links for dyslexic users.
- Adds empty state illustration for pending job.
## 10.4.4 (2018-02-16)
### Security (1 change)

View File

@ -1 +1 @@
0.81.0
0.82.0

View File

@ -1 +1 @@
10.5.0-pre
10.6.0-pre

View File

@ -9,7 +9,7 @@ const Api = {
projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels',
groupLabelsPath: '/groups/:namespace_path/labels',
groupLabelsPath: '/groups/:namespace_path/-/labels',
licensePath: '/api/:version/templates/licenses/:key',
gitignorePath: '/api/:version/templates/gitignores/:key',
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
@ -32,7 +32,7 @@ const Api = {
},
// Return groups list. Filtered by query
groups(query, options, callback) {
groups(query, options, callback = $.noop) {
const url = Api.buildUrl(Api.groupsPath);
return axios.get(url, {
params: Object.assign({

View File

@ -7,7 +7,6 @@
mixins: [
pipelinesMixin,
],
props: {
endpoint: {
type: String,

View File

@ -37,7 +37,7 @@
>
<div class="item-details">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
<user-avatar-image :img-src="mergeRequest.author.avatarUrl" />
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}

View File

@ -1,7 +1,7 @@
import Vue from 'vue';
import deployKeysApp from './components/app.vue';
document.addEventListener('DOMContentLoaded', () => new Vue({
export default () => new Vue({
el: document.getElementById('js-deploy-keys'),
components: {
deployKeysApp,
@ -18,4 +18,4 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
},
});
},
}));
});

View File

@ -71,7 +71,7 @@ export default () => {
el: '#resolve-count-app',
components: {
'resolve-count': ResolveCount
}
},
});
$(window).trigger('resize.nav');

View File

@ -42,190 +42,34 @@ var Dispatcher;
});
});
switch (page) {
case 'projects:merge_requests:index':
case 'projects:issues:index':
case 'projects:issues:show':
case 'projects:issues:new':
case 'projects:issues:edit':
case 'projects:merge_requests:creations:new':
case 'projects:merge_requests:creations:diffs':
case 'projects:merge_requests:edit':
case 'projects:merge_requests:show':
case 'projects:commit:show':
case 'projects:activity':
case 'projects:commits:show':
case 'projects:show':
shortcut_handler = true;
break;
case 'groups:activity':
import('./pages/groups/activity')
.then(callDefault)
.catch(fail);
break;
case 'groups:show':
shortcut_handler = true;
break;
case 'groups:group_members:index':
import('./pages/groups/group_members/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:project_members:index':
import('./pages/projects/project_members')
.then(callDefault)
.catch(fail);
break;
case 'groups:create':
case 'groups:new':
import('./pages/groups/new')
.then(callDefault)
.catch(fail);
break;
case 'groups:edit':
import('./pages/groups/edit')
.then(callDefault)
.catch(fail);
break;
case 'admin:groups:create':
case 'admin:groups:new':
import('./pages/admin/groups/new')
.then(callDefault)
.catch(fail);
break;
case 'admin:groups:edit':
import('./pages/admin/groups/edit')
.then(callDefault)
.catch(fail);
break;
case 'projects:tree:show':
import('./pages/projects/tree/show')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:find_file:show':
import('./pages/projects/find_file/show')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:blob:show':
import('./pages/projects/blob/show')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:blame:show':
import('./pages/projects/blame/show')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'groups:labels:new':
import('./pages/groups/labels/new')
.then(callDefault)
.catch(fail);
break;
case 'groups:labels:edit':
import('./pages/groups/labels/edit')
.then(callDefault)
.catch(fail);
break;
case 'projects:labels:new':
import('./pages/projects/labels/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:labels:edit':
import('./pages/projects/labels/edit')
.then(callDefault)
.catch(fail);
break;
case 'groups:labels:index':
import('./pages/groups/labels/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:labels:index':
import('./pages/projects/labels/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:network:show':
// Ensure we don't create a particular shortcut handler here. This is
// already created, where the network graph is created.
shortcut_handler = true;
break;
case 'projects:forks:new':
import('./pages/projects/forks/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:artifacts:browse':
import('./pages/projects/artifacts/browse')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:artifacts:file':
import('./pages/projects/artifacts/file')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'search:show':
import('./pages/search/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:settings:repository:show':
import('./pages/projects/settings/repository/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:settings:ci_cd:show':
import('./pages/projects/settings/ci_cd/show')
.then(callDefault)
.catch(fail);
break;
case 'groups:settings:ci_cd:show':
import('./pages/groups/settings/ci_cd/show')
.then(callDefault)
.catch(fail);
break;
case 'ci:lints:create':
case 'ci:lints:show':
import('./pages/ci/lints')
.then(callDefault)
.catch(fail);
break;
case 'admin:conversational_development_index:show':
import('./pages/admin/conversational_development_index/show')
.then(callDefault)
.catch(fail);
break;
case 'import:fogbugz:new_user_map':
import('./pages/import/fogbugz/new_user_map')
.then(callDefault)
.catch(fail);
break;
case 'profiles:personal_access_tokens:index':
import('./pages/profiles/personal_access_tokens')
.then(callDefault)
.catch(fail);
break;
case 'admin:impersonation_tokens:index':
import('./pages/admin/impersonation_tokens')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:groups:index':
import('./pages/dashboard/groups/index')
.then(callDefault)
.catch(fail);
break;
const shortcutHandlerPages = [
'projects:activity',
'projects:artifacts:browse',
'projects:artifacts:file',
'projects:blame:show',
'projects:blob:show',
'projects:commit:show',
'projects:commits:show',
'projects:find_file:show',
'projects:issues:edit',
'projects:issues:index',
'projects:issues:new',
'projects:issues:show',
'projects:merge_requests:creations:diffs',
'projects:merge_requests:creations:new',
'projects:merge_requests:edit',
'projects:merge_requests:index',
'projects:merge_requests:show',
'projects:network:show',
'projects:show',
'projects:tree:show',
'groups:show',
];
if (shortcutHandlerPages.indexOf(page) !== -1) {
shortcut_handler = true;
}
switch (path[0]) {
case 'admin':
switch (path[1]) {

View File

@ -14,7 +14,6 @@ export default class DropdownUser extends FilteredSearchDropdown {
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
searchKey: 'search',
params: {
per_page: 20,
active: true,
group_id: this.getGroupId(),
project_id: this.getProjectId(),

View File

@ -111,6 +111,9 @@ export default class FilteredSearchDropdown {
if (hook) {
const data = hook.list.data || [];
if (!data) return;
const results = data.map((o) => {
const updated = o;
updated.droplab_hidden = false;

View File

@ -607,7 +607,20 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.renderItem = function(data, group, index) {
var field, fieldName, html, selected, text, url, value;
var field, fieldName, html, selected, text, url, value, rowHidden;
if (!this.options.renderRow) {
value = this.options.id ? this.options.id(data) : data.id;
if (value) {
value = value.toString().replace(/'/g, '\\\'');
}
}
// Hide element
if (this.options.hideRow && this.options.hideRow(value)) {
rowHidden = true;
}
if (group == null) {
group = false;
}
@ -616,6 +629,7 @@ GitLabDropdown = (function() {
index = false;
}
html = document.createElement('li');
if (data === 'divider' || data === 'separator') {
html.className = data;
return html;
@ -631,11 +645,9 @@ GitLabDropdown = (function() {
html = this.options.renderRow.call(this.options, data, this);
} else {
if (!selected) {
value = this.options.id ? this.options.id(data) : data.id;
fieldName = this.options.fieldName;
if (value) {
value = value.toString().replace(/'/g, '\\\'');
field = this.dropdown.parent().find(`input[name='${fieldName}'][value='${value}']`);
if (field.length) {
selected = true;

View File

@ -30,11 +30,11 @@
default: 'bottom',
},
/**
* value could either be number or string
* as `memberCount` is always passed as string
* while `subgroupCount` & `projectCount`
* are always number
*/
* value could either be number or string
* as `memberCount` is always passed as string
* while `subgroupCount` & `projectCount`
* are always number
*/
value: {
type: [Number, String],
required: false,

View File

@ -316,9 +316,9 @@ export default class LabelsSelect {
},
multiSelect: $dropdown.hasClass('js-multiselect'),
vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(options) {
const { $el, e, isMarking } = options;
const label = options.selectedObj;
clicked: function (clickEvent) {
const { $el, e, isMarking } = clickEvent;
const label = clickEvent.selectedObj;
var isIssueIndex, isMRIndex, page, boardsModel;
var fadeOutLoader = () => {

View File

@ -418,6 +418,16 @@ export const convertObjectPropsToCamelCase = (obj = {}) => {
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
// Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input
$(selector).on('focusin', function selectOnFocusCallback() {
$(this).select().one('mouseup', (e) => {
e.preventDefault();
});
});
};
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),

View File

@ -10,7 +10,7 @@ window.jQuery = jQuery;
window.$ = jQuery;
// lib/utils
import { handleLocationHash } from './lib/utils/common_utils';
import { handleLocationHash, addSelectOnFocusBehaviour } from './lib/utils/common_utils';
import { localTimeAgo } from './lib/utils/datetime_utility';
import { getLocationHash, visitUrl } from './lib/utils/url_utility';
@ -104,13 +104,7 @@ document.addEventListener('DOMContentLoaded', () => {
return true;
});
// Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input
$('.js-select-on-focus').on('focusin', function selectOnFocusCallback() {
$(this).select().one('mouseup', (e) => {
e.preventDefault();
});
});
addSelectOnFocusBehaviour('.js-select-on-focus');
$('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() {
$(this).tooltip('destroy')

View File

@ -1,3 +1,3 @@
import UserCallout from '../../../../user_callout';
import UserCallout from '~/user_callout';
export default () => new UserCallout();
document.addEventListener('DOMContentLoaded', () => new UserCallout());

View File

@ -1,3 +1,3 @@
import groupAvatar from '../../../../group_avatar';
import groupAvatar from '~/group_avatar';
export default () => groupAvatar();
document.addEventListener('DOMContentLoaded', groupAvatar);

View File

@ -2,8 +2,8 @@ import BindInOut from '../../../../behaviors/bind_in_out';
import Group from '../../../../group';
import groupAvatar from '../../../../group_avatar';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
BindInOut.initAll();
new Group(); // eslint-disable-line no-new
groupAvatar();
};
});

View File

@ -1,3 +1,3 @@
import DueDateSelectors from '../../../due_date_select';
import DueDateSelectors from '~/due_date_select';
export default () => new DueDateSelectors();
document.addEventListener('DOMContentLoaded', () => new DueDateSelectors());

View File

@ -0,0 +1,3 @@
import CILintEditor from '../ci_lint_editor';
document.addEventListener('DOMContentLoaded', () => new CILintEditor());

View File

@ -1,3 +0,0 @@
import CILintEditor from './ci_lint_editor';
export default () => new CILintEditor();

View File

@ -0,0 +1,3 @@
import CILintEditor from '../ci_lint_editor';
document.addEventListener('DOMContentLoaded', () => new CILintEditor());

View File

@ -1,3 +1,3 @@
import initGroupsList from '~/groups';
export default initGroupsList;
document.addEventListener('DOMContentLoaded', initGroupsList);

View File

@ -1,3 +1,3 @@
import Activities from '~/activities';
export default () => new Activities();
document.addEventListener('DOMContentLoaded', () => new Activities());

View File

@ -1,7 +1,7 @@
import groupAvatar from '~/group_avatar';
import TransferDropdown from '~/groups/transfer_dropdown';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
groupAvatar();
new TransferDropdown(); // eslint-disable-line no-new
};
});

View File

@ -4,8 +4,8 @@ import memberExpirationDate from '~/member_expiration_date';
import Members from '~/members';
import UsersSelect from '~/users_select';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
memberExpirationDate();
new Members();
new UsersSelect();
};
});

View File

@ -1,3 +1,3 @@
import Labels from '~/labels';
export default () => new Labels();
document.addEventListener('DOMContentLoaded', () => new Labels());

View File

@ -1,3 +1,3 @@
import initLabels from '~/init_labels';
export default initLabels;
document.addEventListener('DOMContentLoaded', initLabels);

View File

@ -1,3 +1,3 @@
import Labels from '~/labels';
export default () => new Labels();
document.addEventListener('DOMContentLoaded', () => new Labels());

View File

@ -2,8 +2,8 @@ import BindInOut from '~/behaviors/bind_in_out';
import Group from '~/group';
import groupAvatar from '~/group_avatar';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
BindInOut.initAll();
new Group(); // eslint-disable-line no-new
groupAvatar();
};
});

View File

@ -1,6 +1,6 @@
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
const variableListEl = document.querySelector('.js-ci-variable-list-section');
// eslint-disable-next-line no-new
new AjaxVariableList({
@ -9,4 +9,4 @@ export default () => {
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
};
});

View File

@ -1,3 +1,3 @@
import UsersSelect from '../../../../users_select';
import UsersSelect from '~/users_select';
export default () => new UsersSelect();
document.addEventListener('DOMContentLoaded', () => new UsersSelect());

View File

@ -1,3 +1,3 @@
import DueDateSelectors from '../../../due_date_select';
import DueDateSelectors from '~/due_date_select';
export default () => new DueDateSelectors();
document.addEventListener('DOMContentLoaded', () => new DueDateSelectors());

View File

@ -1,7 +1,7 @@
import BuildArtifacts from '~/build_artifacts';
import ShortcutsNavigation from '~/shortcuts_navigation';
export default function () {
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new BuildArtifacts(); // eslint-disable-line no-new
}
});

View File

@ -1,7 +1,7 @@
import BlobViewer from '~/blob/viewer/index';
import ShortcutsNavigation from '~/shortcuts_navigation';
export default function () {
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
}
});

View File

@ -1,3 +1,3 @@
import initBlob from '~/pages/projects/init_blob';
export default initBlob;
document.addEventListener('DOMContentLoaded', initBlob);

View File

@ -1,7 +1,7 @@
import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
initBlob();
};
});

View File

@ -1,7 +1,7 @@
import ProjectFindFile from '~/project_find_file';
import ShortcutsFindFile from '~/shortcuts_find_file';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
const findElement = document.querySelector('.js-file-finder');
const projectFindFile = new ProjectFindFile($('.file-finder-holder'), {
url: findElement.dataset.fileFindUrl,
@ -9,4 +9,4 @@ export default () => {
blobUrlTemplate: findElement.dataset.blobUrlTemplate,
});
new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new
};
});

View File

@ -1,5 +1,3 @@
import ProjectFork from '~/project_fork';
export default () => {
new ProjectFork(); // eslint-disable-line no-new
};
document.addEventListener('DOMContentLoaded', () => new ProjectFork());

View File

@ -3,6 +3,7 @@ import Issue from '~/issue';
import ShortcutsIssuable from '~/shortcuts_issuable';
import ZenMode from '~/zen_mode';
import '~/notes/index';
import '~/issue_show/index';
document.addEventListener('DOMContentLoaded', () => {
new Issue(); // eslint-disable-line no-new

View File

@ -1,3 +1,3 @@
import Labels from '~/labels';
export default () => new Labels();
document.addEventListener('DOMContentLoaded', () => new Labels());

View File

@ -1,3 +1,3 @@
import initLabels from '~/init_labels';
export default initLabels;
document.addEventListener('DOMContentLoaded', initLabels);

View File

@ -1,3 +1,3 @@
import Labels from '~/labels';
export default () => new Labels();
document.addEventListener('DOMContentLoaded', () => new Labels());

View File

@ -50,7 +50,7 @@ export default class Project {
Project.projectSelectDropdown();
}
static projectSelectDropdown () {
static projectSelectDropdown() {
projectSelect();
$('.project-item-select').on('click', e => Project.changeProject($(e.currentTarget).val()));
}

View File

@ -3,10 +3,10 @@ import UsersSelect from '../../../users_select';
import groupsSelect from '../../../groups_select';
import Members from '../../../members';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
memberExpirationDate('.js-access-expiration-date-groups');
groupsSelect();
memberExpirationDate();
new Members(); // eslint-disable-line no-new
new UsersSelect(); // eslint-disable-line no-new
};
});

View File

@ -2,7 +2,7 @@ import initSettingsPanels from '~/settings_panels';
import SecretValues from '~/behaviors/secret_values';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
export default function () {
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
@ -22,4 +22,4 @@ export default function () {
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
}
});

View File

@ -1,3 +1,7 @@
import initSettingsPanels from '~/settings_panels';
import initDeployKeys from '~/deploy_keys';
export default initSettingsPanels;
document.addEventListener('DOMContentLoaded', () => {
initDeployKeys();
initSettingsPanels();
});

View File

@ -7,7 +7,7 @@ import BlobViewer from '../../../../blob/viewer';
import NewCommitForm from '../../../../new_commit_form';
import { ajaxGet } from '../../../../lib/utils/common_utils';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new TreeView(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
@ -35,5 +35,4 @@ export default () => {
},
});
}
};
});

View File

@ -1,3 +1,3 @@
import Search from './search';
export default () => new Search();
document.addEventListener('DOMContentLoaded', () => new Search());

View File

@ -31,10 +31,14 @@
type: String,
required: true,
},
id: {
pipelineId: {
type: Number,
required: true,
},
type: {
type: String,
required: true,
},
},
data() {
return {
@ -46,17 +50,27 @@
return `btn ${this.cssClass}`;
},
},
created() {
// We're using eventHub to listen to the modal here instead of
// using props because it would would make the parent components
// much more complex to keep track of the loading state of each button
eventHub.$on('postAction', this.setLoading);
},
beforeDestroy() {
eventHub.$off('postAction', this.setLoading);
},
methods: {
onClick() {
eventHub.$emit('actionConfirmationModal', {
id: this.id,
callback: this.makeRequest,
eventHub.$emit('openConfirmationModal', {
pipelineId: this.pipelineId,
endpoint: this.endpoint,
type: this.type,
});
},
makeRequest() {
this.isLoading = true;
eventHub.$emit('postAction', this.endpoint);
setLoading(endpoint) {
if (endpoint === this.endpoint) {
this.isLoading = true;
}
},
},
};

View File

@ -7,7 +7,6 @@
jobComponent,
dropdownJobComponent,
},
props: {
title: {
type: String,

View File

@ -1,7 +1,8 @@
<script>
import modal from '~/vue_shared/components/modal.vue';
import { s__, sprintf } from '~/locale';
import pipelinesTableRowComponent from './pipelines_table_row.vue';
import stopConfirmationModal from './stop_confirmation_modal.vue';
import retryConfirmationModal from './retry_confirmation_modal.vue';
import eventHub from '../event_hub';
/**
* Pipelines Table Component.
@ -11,8 +12,7 @@
export default {
components: {
pipelinesTableRowComponent,
stopConfirmationModal,
retryConfirmationModal,
modal,
},
props: {
pipelines: {
@ -33,6 +33,52 @@
required: true,
},
},
data() {
return {
pipelineId: '',
endpoint: '',
type: '',
};
},
computed: {
modalTitle() {
return this.type === 'stop' ?
sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), {
pipelineId: `'${this.pipelineId}'`,
}, false) :
sprintf(s__('Pipeline|Retry pipeline #%{pipelineId}?'), {
pipelineId: `'${this.pipelineId}'`,
}, false);
},
modalText() {
return this.type === 'stop' ?
sprintf(s__('Pipeline|Youre about to stop pipeline %{pipelineId}.'), {
pipelineId: `<strong>#${this.pipelineId}</strong>`,
}, false) :
sprintf(s__('Pipeline|Youre about to retry pipeline %{pipelineId}.'), {
pipelineId: `<strong>#${this.pipelineId}</strong>`,
}, false);
},
primaryButtonLabel() {
return this.type === 'stop' ? s__('Pipeline|Stop pipeline') : s__('Pipeline|Retry pipeline');
},
},
created() {
eventHub.$on('openConfirmationModal', this.setModalData);
},
beforeDestroy() {
eventHub.$off('openConfirmationModal', this.setModalData);
},
methods: {
setModalData(data) {
this.pipelineId = data.pipelineId;
this.endpoint = data.endpoint;
this.type = data.type;
},
onSubmit() {
eventHub.$emit('postAction', this.endpoint);
},
},
};
</script>
<template>
@ -74,7 +120,20 @@
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/>
<stop-confirmation-modal />
<retry-confirmation-modal />
<modal
id="confirmation-modal"
:title="modalTitle"
:text="modalText"
kind="danger"
:primary-button-label="primaryButtonLabel"
@submit="onSubmit"
>
<template
slot="body"
slot-scope="props"
>
<p v-html="props.text"></p>
</template>
</modal>
</div>
</template>

View File

@ -223,7 +223,8 @@
<div class="table-section section-10 commit-link">
<div
class="table-mobile-header"
role="rowheader">
role="rowheader"
>
Status
</div>
<div class="table-mobile-content">
@ -305,9 +306,10 @@
css-class="js-pipelines-retry-button btn-default btn-retry"
title="Retry"
icon="repeat"
:id="pipeline.id"
:pipeline-id="pipeline.id"
data-toggle="modal"
data-target="#retry-confirmation-modal"
data-target="#confirmation-modal"
type="retry"
/>
<async-button-component
@ -316,9 +318,10 @@
css-class="js-pipelines-cancel-button btn-remove"
title="Cancel"
icon="close"
:id="pipeline.id"
:pipeline-id="pipeline.id"
data-toggle="modal"
data-target="#stop-confirmation-modal"
data-target="#confirmation-modal"
type="stop"
/>
</div>
</div>

View File

@ -1,65 +0,0 @@
<script>
import modal from '~/vue_shared/components/modal.vue';
import { s__, sprintf } from '~/locale';
import eventHub from '../event_hub';
export default {
components: {
modal,
},
data() {
return {
id: '',
callback: () => {},
};
},
computed: {
title() {
return sprintf(s__('Pipeline|Retry pipeline #%{id}?'), {
id: `'${this.id}'`,
}, false);
},
text() {
return sprintf(s__('Pipeline|Youre about to retry pipeline %{id}.'), {
id: `<strong>#${this.id}</strong>`,
}, false);
},
primaryButtonLabel() {
return s__('Pipeline|Retry pipeline');
},
},
created() {
eventHub.$on('actionConfirmationModal', this.updateModal);
},
beforeDestroy() {
eventHub.$off('actionConfirmationModal', this.updateModal);
},
methods: {
updateModal(action) {
this.id = action.id;
this.callback = action.callback;
},
onSubmit() {
this.callback();
},
},
};
</script>
<template>
<modal
id="retry-confirmation-modal"
:title="title"
:text="text"
kind="danger"
:primary-button-label="primaryButtonLabel"
@submit="onSubmit"
>
<template
slot="body"
slot-scope="props"
>
<p v-html="props.text"></p>
</template>
</modal>
</template>

View File

@ -50,9 +50,7 @@
computed: {
dropdownClass() {
return this.dropdownContent.length > 0 ?
'js-builds-dropdown-container' :
'js-builds-dropdown-loading';
return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading';
},
triggerButtonClass() {

View File

@ -1,65 +0,0 @@
<script>
import modal from '~/vue_shared/components/modal.vue';
import { s__, sprintf } from '~/locale';
import eventHub from '../event_hub';
export default {
components: {
modal,
},
data() {
return {
id: '',
callback: () => {},
};
},
computed: {
title() {
return sprintf(s__('Pipeline|Stop pipeline #%{id}?'), {
id: `'${this.id}'`,
}, false);
},
text() {
return sprintf(s__('Pipeline|Youre about to stop pipeline %{id}.'), {
id: `<strong>#${this.id}</strong>`,
}, false);
},
primaryButtonLabel() {
return s__('Pipeline|Stop pipeline');
},
},
created() {
eventHub.$on('actionConfirmationModal', this.updateModal);
},
beforeDestroy() {
eventHub.$off('actionConfirmationModal', this.updateModal);
},
methods: {
updateModal(action) {
this.id = action.id;
this.callback = action.callback;
},
onSubmit() {
this.callback();
},
},
};
</script>
<template>
<modal
id="stop-confirmation-modal"
:title="title"
:text="text"
kind="danger"
:primary-button-label="primaryButtonLabel"
@submit="onSubmit"
>
<template
slot="body"
slot-scope="props"
>
<p v-html="props.text"></p>
</template>
</modal>
</template>

View File

@ -73,7 +73,7 @@ export default class ProjectFindFile {
// find file
}
// files pathes load
// files pathes load
load(url) {
axios.get(url)
.then(({ data }) => {
@ -85,7 +85,7 @@ export default class ProjectFindFile {
.catch(() => flash(__('An error occurred while loading filenames')));
}
// render result
// render result
renderList(filePaths, searchText) {
var blobItemUrl, filePath, html, i, j, len, matches, results;
this.element.find(".tree-table > tbody").empty();

View File

@ -1,3 +1,5 @@
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
let hasUserDefinedProjectPath = false;
const deriveProjectPathFromUrl = ($projectImportUrl) => {
@ -36,6 +38,7 @@ const bindEvents = () => {
const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon svg');
const $templateProjectNameInput = $('#template-project-name #project_path');
const $pushNewProjectTipTrigger = $('.push-new-project-tip');
if ($newProjectForm.length !== 1) {
return;
@ -55,6 +58,34 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
});
if ($pushNewProjectTipTrigger) {
$pushNewProjectTipTrigger
.removeAttr('rel')
.removeAttr('target')
.on('click', (e) => { e.preventDefault(); })
.popover({
title: $pushNewProjectTipTrigger.data('title'),
placement: 'auto bottom',
html: 'true',
content: $('.push-new-project-tip-template').html(),
})
.on('shown.bs.popover', () => {
$(document).on('click.popover touchstart.popover', (event) => {
if ($(event.target).closest('.popover').length === 0) {
$pushNewProjectTipTrigger.trigger('click');
}
});
const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus');
addSelectOnFocusBehaviour(target);
target.focus();
})
.on('hide.bs.popover', () => {
$(document).off('click.popover touchstart.popover');
});
}
function chooseTemplate() {
$('.template-option').hide();
$projectFieldsForm.addClass('selected');

View File

@ -1,6 +1,7 @@
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
import syntaxHighlight from './syntax_highlight';
// Render Gitlab flavoured Markdown
//
// Delegates to syntax highlight and render math & mermaid diagrams.

View File

@ -1,5 +1,5 @@
<script>
import Flash from '../../../flash';
import Flash from '~/flash';
import editForm from './edit_form.vue';
import issuableMixin from '../../../vue_shared/mixins/issuable';
import Icon from '../../../vue_shared/components/icon.vue';
@ -53,8 +53,7 @@
discussion_locked: locked,
})
.then(() => location.reload())
.catch(() => Flash(this.__(`Something went wrong trying to
change the locked state of this ${this.issuableDisplayName}`)));
.catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`)));
},
},
};

View File

@ -39,7 +39,6 @@ function UsersSelect(currentUser, els, options = {}) {
options.showCurrentUser = $dropdown.data('currentUser');
options.todoFilter = $dropdown.data('todoFilter');
options.todoStateFilter = $dropdown.data('todoStateFilter');
options.perPage = $dropdown.data('perPage');
showNullUser = $dropdown.data('nullUser');
defaultNullUser = $dropdown.data('nullUserDefault');
showMenuAbove = $dropdown.data('showMenuAbove');
@ -669,7 +668,6 @@ UsersSelect.prototype.users = function(query, options, callback) {
const url = this.buildUrl(this.usersPath);
const params = {
search: query,
per_page: options.perPage || 20,
active: true,
project_id: options.projectId || null,
group_id: options.groupId || null,

View File

@ -107,7 +107,8 @@
<template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest">
<div
class="accept-merge-holder clearfix
js-toggle-container accept-action media space-children">
js-toggle-container accept-action media space-children"
>
<button
type="button"
class="btn btn-sm btn-reopen btn-success"

View File

@ -96,9 +96,7 @@ export default {
cb.call(null, data);
}
})
.catch(() => {
new Flash('Something went wrong. Please try again.'); // eslint-disable-line
});
.catch(() => new Flash('Something went wrong. Please try again.'));
},
initPolling() {
this.pollingInterval = new SmartInterval({
@ -146,9 +144,7 @@ export default {
Project.initRefSwitcher();
}
})
.catch(() => {
new Flash('Something went wrong. Please try again.'); // eslint-disable-line
});
.catch(() => new Flash('Something went wrong. Please try again.'));
},
handleNotification(data) {
if (data.ci_status === this.mr.ciStatus) return;

View File

@ -4,7 +4,6 @@ import { stateKey } from './state_maps';
import { formatDate } from '../../lib/utils/datetime_utility';
export default class MergeRequestStore {
constructor(data) {
this.sha = data.diff_head_sha;
this.gitlabLogo = data.gitlabLogo;
@ -169,5 +168,4 @@ export default class MergeRequestStore {
return timeagoInstance.format(date);
}
}

View File

@ -6,12 +6,12 @@
import userAvatarImage from './user_avatar/user_avatar_image.vue';
/**
* Renders header component for job and pipeline page based on UI mockups
*
* Used in:
* - job show page
* - pipeline show page
*/
* Renders header component for job and pipeline page based on UI mockups
*
* Used in:
* - job show page
* - pipeline show page
*/
export default {
components: {
ciIconBadge,
@ -118,7 +118,8 @@
<section
class="header-action-buttons"
v-if="actions.length">
v-if="actions.length"
>
<template
v-for="(action, i) in actions"
>

View File

@ -1,6 +1,5 @@
<script>
/* eslint-disable vue/require-default-prop */
/* This is a re-usable vue component for rendering a button
that will probably be sending off ajax requests and need
to show the loading status by setting the `loading` option.

View File

@ -65,7 +65,8 @@
</li>
<li
class="md-header-tab"
:class="{ active: previewMarkdown }">
:class="{ active: previewMarkdown }"
>
<a
class="js-preview-link"
href="#md-preview-holder"

View File

@ -13,6 +13,12 @@
props: {
/**
This function will take the information given by the pagination component
Here is an example `change` method:
change(pagenum) {
gl.utils.visitUrl(`?page=${pagenum}`);
},
*/
change: {
type: Function,

View File

@ -444,6 +444,19 @@
}
}
.btn-missing {
color: $notes-light-color;
border: 1px dashed $border-gray-normal-dashed;
border-radius: $border-radius-default;
&:hover,
&:active,
&:focus {
color: $notes-light-color;
background-color: $white-normal;
}
}
.btn-svg svg {
@include btn-svg;
}

View File

@ -63,10 +63,6 @@
}
}
.project-stats {
display: none;
}
.group-buttons {
display: none;
}

View File

@ -3,7 +3,6 @@
transition: padding $sidebar-transition-duration;
.container-fluid {
background: $white-light;
padding: 0 $gl-padding;
&.container-blank {

View File

@ -296,7 +296,7 @@ body {
line-height: 1.3;
font-size: 1.25em;
font-weight: $gl-font-weight-bold;
margin: 12px 7px;
margin: 12px 0;
}
h1,
@ -333,6 +333,10 @@ a > code {
font-family: $monospace_font;
}
.weight-normal {
font-weight: $gl-font-weight-normal;
}
.commit-sha,
.ref-name {
@extend .monospace;

View File

@ -215,8 +215,8 @@ $tooltip-font-size: 12px;
*/
$gl-padding: 16px;
$gl-padding-8: 8px;
$gl-padding-4: 4px;
$gl-col-padding: 15px;
$gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top: 10px;
@ -377,6 +377,10 @@ $inactive-badge-background: rgba(0, 0, 0, .08);
$btn-active-gray: #ececec;
$btn-active-gray-light: e4e7ed;
$btn-white-active: #848484;
$gl-btn-padding: 10px;
$gl-btn-line-height: 16px;
$gl-btn-vert-padding: 8px;
$gl-btn-horz-padding: 12px;
/*
* Badges

View File

@ -678,6 +678,9 @@ a.deploy-project-label {
}
}
.project-empty-note-panel {
border-bottom: 1px solid $border-color;
}
.project-stats {
font-size: 0;
@ -686,11 +689,13 @@ a.deploy-project-label {
border-bottom: 1px solid $border-color;
.nav {
padding-top: 12px;
padding-bottom: 12px;
margin-top: $gl-padding-8;
margin-bottom: $gl-padding-8;
> li {
display: inline-block;
margin-top: $gl-padding-4;
margin-bottom: $gl-padding-4;
&:not(:last-child) {
margin-right: $gl-padding;
@ -704,36 +709,32 @@ a.deploy-project-label {
float: right;
}
}
}
> a {
padding: 0;
background-color: transparent;
font-size: 14px;
line-height: 29px;
color: $notes-light-color;
.stat-text,
.stat-link {
padding: $gl-btn-vert-padding 0;
background-color: transparent;
font-size: $gl-font-size;
line-height: $gl-btn-line-height;
color: $notes-light-color;
}
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
}
.stat-link {
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
}
}
}
li.missing {
border: 1px dashed $border-gray-normal-dashed;
border-radius: $border-radius-default;
a {
padding-left: 10px;
padding-right: 10px;
color: $notes-light-color;
display: block;
.btn {
padding: $gl-btn-vert-padding $gl-btn-horz-padding;
line-height: $gl-btn-line-height;
}
&:hover {
background-color: $gray-normal;
.btn-missing {
@extend .btn-missing;
}
}
}
@ -743,7 +744,7 @@ pre.light-well {
}
.git-empty {
margin: 0 7px 7px;
margin-bottom: 7px;
h5 {
color: $gl-text-color;
@ -895,6 +896,12 @@ pre.light-well {
}
}
.project-tip-command {
> .input-group-btn:first-child {
width: auto;
}
}
.protected-branches-list,
.protected-tags-list {
margin-bottom: 30px;

View File

@ -126,10 +126,15 @@ class ApplicationController < ActionController::Base
Ability.allowed?(object, action, subject)
end
def access_denied!
def access_denied!(message = nil)
respond_to do |format|
format.json { head :not_found }
format.any { render "errors/access_denied", layout: "errors", status: 404 }
format.any { head :not_found }
format.html do
render "errors/access_denied",
layout: "errors",
status: 404,
locals: { message: message }
end
end
end

View File

@ -55,7 +55,7 @@ module Boards
end
def issue
@issue ||= issues_finder.execute.find(params[:id])
@issue ||= issues_finder.find(params[:id])
end
def filter_params

View File

@ -0,0 +1,24 @@
module ControllerWithCrossProjectAccessCheck
extend ActiveSupport::Concern
included do
extend Gitlab::CrossProjectAccess::ClassMethods
before_action :cross_project_check
end
def cross_project_check
if Gitlab::CrossProjectAccess.find_check(self)&.should_run?(self)
authorize_cross_project_page!
end
end
def authorize_cross_project_page!
return if can?(current_user, :read_cross_project)
rejection_message = _(
"This page is unavailable because you are not allowed to read information "\
"across multiple projects."
)
access_denied!(rejection_message)
end
end

View File

@ -3,16 +3,20 @@ module RoutableActions
def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil)
routable = routable_klass.find_by_full_path(requested_full_path, follow_redirects: request.get?)
if routable_authorized?(routable, extra_authorization_proc)
ensure_canonical_path(routable, requested_full_path)
routable
else
route_not_found
handle_not_found_or_authorized(routable)
nil
end
end
# This is overridden in gitlab-ee.
def handle_not_found_or_authorized(_routable)
route_not_found
end
def routable_authorized?(routable, extra_authorization_proc)
action = :"read_#{routable.class.to_s.underscore}"
return false unless can?(current_user, action, routable)

View File

@ -24,7 +24,7 @@ module UploadsActions
# - or redirect to its URL
#
def show
return render_404 unless uploader.exists?
return render_404 unless uploader&.exists?
if uploader.file_storage?
disposition = uploader.image_or_video? ? 'inline' : 'attachment'
@ -71,6 +71,9 @@ module UploadsActions
def build_uploader_from_params
uploader = uploader_class.new(model, secret: params[:secret])
return nil unless uploader.model_valid?
uploader.retrieve_from_store!(params[:filename])
uploader
end

View File

@ -1,6 +1,10 @@
class Dashboard::ApplicationController < ApplicationController
include ControllerWithCrossProjectAccessCheck
layout 'dashboard'
requires_cross_project_access
private
def projects

View File

@ -1,6 +1,8 @@
class Dashboard::GroupsController < Dashboard::ApplicationController
include GroupTree
skip_cross_project_access_check :index
def index
groups = GroupsFinder.new(current_user, all_available: false).execute
render_group_tree(groups)

View File

@ -4,6 +4,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
before_action :set_non_archived_param
before_action :default_sorting
skip_cross_project_access_check :index, :starred
def index
@projects = load_projects(params.merge(non_public: true)).page(params[:page])

View File

@ -1,4 +1,6 @@
class Dashboard::SnippetsController < Dashboard::ApplicationController
skip_cross_project_access_check :index
def index
@snippets = SnippetsFinder.new(
current_user,

View File

@ -1,10 +1,12 @@
class Groups::ApplicationController < ApplicationController
include RoutableActions
include ControllerWithCrossProjectAccessCheck
layout 'group'
skip_before_action :authenticate_user!
before_action :group
requires_cross_project_access
private

View File

@ -1,6 +1,8 @@
class Groups::AvatarsController < Groups::ApplicationController
before_action :authorize_admin_group!
skip_cross_project_access_check :destroy
def destroy
@group.remove_avatar!
@group.save

View File

@ -1,6 +1,7 @@
module Groups
class ChildrenController < Groups::ApplicationController
before_action :group
skip_cross_project_access_check :index
def index
parent = if params[:parent_id].present?

View File

@ -6,6 +6,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access]
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite,
:override
def index
@sort = params[:sort].presence || sort_value_name
@project = @group.projects.find(params[:project_id]) if params[:project_id]

View File

@ -1,6 +1,7 @@
module Groups
module Settings
class CiCdController < Groups::ApplicationController
skip_cross_project_access_check :show
before_action :authorize_admin_pipeline!
def show

View File

@ -2,6 +2,8 @@ module Groups
class VariablesController < Groups::ApplicationController
before_action :authorize_admin_build!
skip_cross_project_access_check :show, :update
def show
respond_to do |format|
format.json do

View File

@ -19,6 +19,12 @@ class GroupsController < Groups::ApplicationController
before_action :user_actions, only: [:show, :subgroups]
skip_cross_project_access_check :index, :new, :create, :edit, :update,
:destroy, :projects
# When loading show as an atom feed, we render events that could leak cross
# project information
skip_cross_project_access_check :show, if: -> { request.format.html? }
layout :determine_layout
def index

View File

@ -1,5 +1,6 @@
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::GonHelper
include Gitlab::Allowable
include PageLayoutHelper
include OauthApplications
@ -8,6 +9,8 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_action :add_gon_variables
before_action :load_scopes, only: [:index, :create, :edit]
helper_method :can?
layout 'profile'
def index

View File

@ -34,9 +34,9 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
def target
case params[:type]&.downcase
when 'issue'
IssuesFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:type_id])
IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:type_id])
when 'mergerequest'
MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:type_id])
MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:type_id])
when 'commit'
@project.commit(params[:type_id])
end

View File

@ -133,7 +133,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def after_edit_path
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:from_merge_request_iid])
if from_merge_request && @branch_name == @ref
diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
"##{hexdigest(@path)}"

View File

@ -40,9 +40,9 @@ class Projects::Clusters::GcpController < Projects::ApplicationController
def verify_billing
case google_project_billing_status
when nil
flash[:alert] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
flash.now[:alert] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
when false
flash[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" }
flash.now[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" }
when true
return
end

View File

@ -75,7 +75,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def branch_to
@target_project = selected_target_project
if params[:ref].present?
if @target_project && params[:ref].present?
@ref = params[:ref]
@commit = @target_project.commit(Gitlab::Git::BRANCH_REF_PREFIX + @ref)
end
@ -85,7 +85,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def update_branches
@target_project = selected_target_project
@target_branches = @target_project.repository.branch_names
@target_branches = @target_project ? @target_project.repository.branch_names : []
render layout: false
end
@ -121,7 +121,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@project
elsif params[:target_project_id].present?
MergeRequestTargetProjectFinder.new(current_user: current_user, source_project: @project)
.execute.find(params[:target_project_id])
.find_by(id: params[:target_project_id])
else
@project.forked_from_project
end

View File

@ -3,7 +3,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
before_action :require_pages_enabled!
before_action :authorize_update_pages!, except: [:show]
before_action :domain, only: [:show, :destroy]
before_action :domain, only: [:show, :destroy, :verify]
def show
end
@ -12,11 +12,23 @@ class Projects::PagesDomainsController < Projects::ApplicationController
@domain = @project.pages_domains.new
end
def verify
result = VerifyPagesDomainService.new(@domain).execute
if result[:status] == :success
flash[:notice] = 'Successfully verified domain ownership'
else
flash[:alert] = 'Failed to verify domain ownership'
end
redirect_to project_pages_domain_path(@project, @domain)
end
def create
@domain = @project.pages_domains.create(pages_domain_params)
if @domain.valid?
redirect_to project_pages_path(@project)
redirect_to project_pages_domain_path(@project, @domain)
else
render 'new'
end
@ -46,6 +58,6 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end
def domain
@domain ||= @project.pages_domains.find_by(domain: params[:id].to_s)
@domain ||= @project.pages_domains.find_by!(domain: params[:id].to_s)
end
end

View File

@ -0,0 +1,27 @@
module Projects
module Prometheus
class MetricsController < Projects::ApplicationController
before_action :authorize_admin_project!
def active_common
respond_to do |format|
format.json do
matched_metrics = prometheus_service.matched_metrics || {}
if matched_metrics.any?
render json: matched_metrics
else
head :no_content
end
end
end
end
private
def prometheus_service
@prometheus_service ||= project.find_or_initialize_service('prometheus')
end
end
end
end

View File

@ -1,24 +0,0 @@
class Projects::PrometheusController < Projects::ApplicationController
before_action :authorize_read_project!
before_action :require_prometheus_metrics!
def active_metrics
respond_to do |format|
format.json do
matched_metrics = project.prometheus_service.matched_metrics || {}
if matched_metrics.any?
render json: matched_metrics
else
head :no_content
end
end
end
end
private
def require_prometheus_metrics!
render_404 unless project.prometheus_service.present?
end
end

View File

@ -45,7 +45,7 @@ class ProjectsController < Projects::ApplicationController
notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name }
)
else
render 'new'
render 'new', locals: { active_tab: ('import' if project_params[:import_url].present?) }
end
end
@ -114,6 +114,8 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format|
format.html do
@notification_setting = current_user.notification_settings_for(@project) if current_user
@project = @project.present(current_user: current_user)
render_landing_page
end

Some files were not shown because too many files have changed in this diff Show More