summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-02-23 11:48:53 +0000
committerFilipa Lacerda <filipa@gitlab.com>2017-02-23 11:48:53 +0000
commit05c66406ca7e7f29b9b210fda9f31a60528917f1 (patch)
tree18d7f553a61fea3c6a9655be16a4842478990284 /app
parent0f36cfd7f58977becea9d3ecf410d3669440fbe9 (diff)
parentf106ad513546c8d77b88a0a061a0b6a7e7ee26ed (diff)
downloadgitlab-ce-26900-pipelines-tabs.tar.gz
Merge branch 'master' into 26900-pipelines-tabs26900-pipelines-tabs
* master: (361 commits) Code style improvements remove require.context from network_bundle remove require.context from graphs_bundle remove require.context from filtered_search_bundle Ignore two Rails CVEs in bundler:audit job Remove Pages readme Change Pages redirect Add missing index.md to Pages docs Added double newline after file upload markdown insert Reorder main index items in Pages overview remove html comments remove <> wrapping text - part 3 wrapping text - part 2 [ci skip] fix link wrap text - part 1 - [ci skip] typo fix spelling, add intermediate cert link Improve `Gitlab::EeCompatCheck` by using the `git apply --3way` flag remove link to unfinished video ...
Diffstat (limited to 'app')
-rwxr-xr-x[-rw-r--r--]app/assets/images/favicon-blue.icobin5430 -> 5430 bytes
-rw-r--r--app/assets/images/icon-merge-request-unmerged.svg1
-rw-r--r--app/assets/images/mailers/gitlab_footer_logo.gifbin0 -> 3654 bytes
-rw-r--r--app/assets/images/mailers/gitlab_header_logo.gifbin0 -> 3040 bytes
-rw-r--r--app/assets/javascripts/admin.js2
-rw-r--r--app/assets/javascripts/api.js2
-rw-r--r--app/assets/javascripts/application.js9
-rw-r--r--app/assets/javascripts/aside.js2
-rw-r--r--app/assets/javascripts/autosave.js2
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/behaviors/autosize.js2
-rw-r--r--app/assets/javascripts/behaviors/details_behavior.js2
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js2
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js2
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js2
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selector.js2
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selectors.js2
-rw-r--r--app/assets/javascripts/blob/blob_license_selector.js2
-rw-r--r--app/assets/javascripts/blob_edit/blob_edit_bundle.js2
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js2
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js.es664
-rw-r--r--app/assets/javascripts/breakpoints.js2
-rw-r--r--app/assets/javascripts/broadcast_message.js2
-rw-r--r--app/assets/javascripts/build.js2
-rw-r--r--app/assets/javascripts/build_artifacts.js2
-rw-r--r--app/assets/javascripts/commit.js2
-rw-r--r--app/assets/javascripts/commit/file.js2
-rw-r--r--app/assets/javascripts/commit/image_file.js2
-rw-r--r--app/assets/javascripts/commits.js2
-rw-r--r--app/assets/javascripts/compare.js2
-rw-r--r--app/assets/javascripts/compare_autocomplete.js.es62
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js2
-rw-r--r--app/assets/javascripts/copy_to_clipboard.js2
-rw-r--r--app/assets/javascripts/create_label.js.es66
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es622
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es647
-rw-r--r--app/assets/javascripts/cycle_analytics/default_event_objects.js.es698
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js.es618
-rw-r--r--app/assets/javascripts/dispatcher.js.es65
-rw-r--r--app/assets/javascripts/dropzone_input.js7
-rw-r--r--app/assets/javascripts/environments/components/environment.js.es65
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.js.es638
-rw-r--r--app/assets/javascripts/environments/components/environment_item.js.es625
-rw-r--r--app/assets/javascripts/environments/environments_bundle.js.es61
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js.es61
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.js.es65
-rw-r--r--app/assets/javascripts/extensions/jquery.js2
-rw-r--r--app/assets/javascripts/files_comment_button.js2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js.es629
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_bundle.js12
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es627
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es66
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js.es615
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es65
-rw-r--r--app/assets/javascripts/flash.js2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es69
-rw-r--r--app/assets/javascripts/gl_dropdown.js5
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js7
-rw-r--r--app/assets/javascripts/graphs/stat_graph.js2
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors.js2
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js2
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_util.js2
-rw-r--r--app/assets/javascripts/group_avatar.js2
-rw-r--r--app/assets/javascripts/groups_select.js2
-rw-r--r--app/assets/javascripts/header.js2
-rw-r--r--app/assets/javascripts/importer_status.js2
-rw-r--r--app/assets/javascripts/issuable_context.js2
-rw-r--r--app/assets/javascripts/issuable_form.js2
-rw-r--r--app/assets/javascripts/issue.js45
-rw-r--r--app/assets/javascripts/issue_status_select.js2
-rw-r--r--app/assets/javascripts/labels.js2
-rw-r--r--app/assets/javascripts/labels_select.js2
-rw-r--r--app/assets/javascripts/layout_nav.js2
-rw-r--r--app/assets/javascripts/lib/cropper.js2
-rw-r--r--app/assets/javascripts/lib/raphael.js2
-rw-r--r--app/assets/javascripts/lib/utils/animate.js2
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js.es62
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js.es62
-rw-r--r--app/assets/javascripts/lib/utils/notify.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js15
-rw-r--r--app/assets/javascripts/lib/utils/type_utility.js2
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js.es62
-rw-r--r--app/assets/javascripts/line_highlighter.js2
-rw-r--r--app/assets/javascripts/logo.js2
-rw-r--r--app/assets/javascripts/member_expiration_date.js.es62
-rw-r--r--app/assets/javascripts/merge_request.js45
-rw-r--r--app/assets/javascripts/merge_request_widget.js.es61
-rw-r--r--app/assets/javascripts/merged_buttons.js2
-rw-r--r--app/assets/javascripts/milestone.js2
-rw-r--r--app/assets/javascripts/milestone_select.js2
-rw-r--r--app/assets/javascripts/mini_pipeline_graph_dropdown.js.es62
-rw-r--r--app/assets/javascripts/namespace_select.js2
-rw-r--r--app/assets/javascripts/network/branch_graph.js2
-rw-r--r--app/assets/javascripts/network/network.js2
-rw-r--r--app/assets/javascripts/network/network_bundle.js7
-rw-r--r--app/assets/javascripts/new_branch_form.js2
-rw-r--r--app/assets/javascripts/new_commit_form.js2
-rw-r--r--app/assets/javascripts/notes.js34
-rw-r--r--app/assets/javascripts/notifications_dropdown.js2
-rw-r--r--app/assets/javascripts/notifications_form.js2
-rw-r--r--app/assets/javascripts/project.js2
-rw-r--r--app/assets/javascripts/project_avatar.js2
-rw-r--r--app/assets/javascripts/project_find_file.js2
-rw-r--r--app/assets/javascripts/project_fork.js2
-rw-r--r--app/assets/javascripts/project_import.js2
-rw-r--r--app/assets/javascripts/project_label_subscription.js.es66
-rw-r--r--app/assets/javascripts/project_new.js2
-rw-r--r--app/assets/javascripts/project_select.js2
-rw-r--r--app/assets/javascripts/project_show.js2
-rw-r--r--app/assets/javascripts/projects_list.js2
-rw-r--r--app/assets/javascripts/render_gfm.js2
-rw-r--r--app/assets/javascripts/render_math.js2
-rw-r--r--app/assets/javascripts/right_sidebar.js20
-rw-r--r--app/assets/javascripts/search.js2
-rw-r--r--app/assets/javascripts/search_autocomplete.js.es64
-rw-r--r--app/assets/javascripts/shortcuts.js2
-rw-r--r--app/assets/javascripts/shortcuts_dashboard_navigation.js2
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js2
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js2
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js2
-rw-r--r--app/assets/javascripts/shortcuts_network.js2
-rw-r--r--app/assets/javascripts/sidebar.js.es6111
-rw-r--r--app/assets/javascripts/single_file_diff.js2
-rw-r--r--app/assets/javascripts/snippet/snippet_bundle.js2
-rw-r--r--app/assets/javascripts/star.js2
-rw-r--r--app/assets/javascripts/subscription_select.js2
-rw-r--r--app/assets/javascripts/syntax_highlight.js2
-rw-r--r--app/assets/javascripts/task_list.js40
-rw-r--r--app/assets/javascripts/todos.js.es6143
-rw-r--r--app/assets/javascripts/tree.js2
-rw-r--r--app/assets/javascripts/u2f/error.js2
-rw-r--r--app/assets/javascripts/u2f/register.js2
-rw-r--r--app/assets/javascripts/u2f/util.js2
-rw-r--r--app/assets/javascripts/users/calendar.js2
-rw-r--r--app/assets/javascripts/users_select.js2
-rw-r--r--app/assets/javascripts/version_check_image.js.es616
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipelines.js.es65
-rw-r--r--app/assets/javascripts/wikis.js.es66
-rw-r--r--app/assets/javascripts/zen_mode.js2
-rw-r--r--app/assets/stylesheets/framework.scss1
-rw-r--r--app/assets/stylesheets/framework/animations.scss3
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss144
-rw-r--r--app/assets/stylesheets/framework/header.scss39
-rw-r--r--app/assets/stylesheets/framework/lists.scss10
-rw-r--r--app/assets/stylesheets/framework/mixins.scss7
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss191
-rw-r--r--app/assets/stylesheets/framework/typography.scss25
-rw-r--r--app/assets/stylesheets/framework/variables.scss4
-rw-r--r--app/assets/stylesheets/pages/environments.scss18
-rw-r--r--app/assets/stylesheets/pages/issuable.scss4
-rw-r--r--app/assets/stylesheets/pages/issues.scss5
-rw-r--r--app/assets/stylesheets/pages/milestone.scss20
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss5
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss39
-rw-r--r--app/assets/stylesheets/pages/projects.scss7
-rw-r--r--app/assets/stylesheets/pages/todos.scss58
-rw-r--r--app/assets/stylesheets/pages/tree.scss2
-rw-r--r--app/assets/stylesheets/print.scss1
-rw-r--r--app/controllers/admin/background_jobs_controller.rb1
-rw-r--r--app/controllers/admin/runners_controller.rb6
-rw-r--r--app/controllers/admin/system_info_controller.rb1
-rw-r--r--app/controllers/admin/users_controller.rb1
-rw-r--r--app/controllers/application_controller.rb8
-rw-r--r--app/controllers/concerns/creates_commit.rb16
-rw-r--r--app/controllers/concerns/issuable_collections.rb20
-rw-r--r--app/controllers/concerns/issues_action.rb2
-rw-r--r--app/controllers/concerns/merge_requests_action.rb2
-rw-r--r--app/controllers/concerns/snippets_actions.rb21
-rw-r--r--app/controllers/concerns/spammable_actions.rb30
-rw-r--r--app/controllers/dashboard/todos_controller.rb17
-rw-r--r--app/controllers/import/fogbugz_controller.rb2
-rw-r--r--app/controllers/import/google_code_controller.rb4
-rw-r--r--app/controllers/invites_controller.rb4
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb9
-rw-r--r--app/controllers/profiles/keys_controller.rb4
-rw-r--r--app/controllers/profiles/preferences_controller.rb1
-rw-r--r--app/controllers/projects/application_controller.rb1
-rw-r--r--app/controllers/projects/blob_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb36
-rw-r--r--app/controllers/projects/merge_requests_controller.rb16
-rw-r--r--app/controllers/projects/runners_controller.rb6
-rw-r--r--app/controllers/projects/snippets_controller.rb34
-rw-r--r--app/controllers/projects/tree_controller.rb4
-rw-r--r--app/controllers/snippets_controller.rb30
-rw-r--r--app/finders/issuable_finder.rb15
-rw-r--r--app/finders/issues_finder.rb4
-rw-r--r--app/finders/merge_requests_finder.rb10
-rw-r--r--app/helpers/emails_helper.rb17
-rw-r--r--app/helpers/namespaces_helper.rb4
-rw-r--r--app/helpers/nav_helper.rb14
-rw-r--r--app/helpers/preferences_helper.rb4
-rw-r--r--app/helpers/todos_helper.rb4
-rw-r--r--app/mailers/emails/pipelines.rb4
-rw-r--r--app/models/ci/build.rb33
-rw-r--r--app/models/ci/pipeline.rb14
-rw-r--r--app/models/ci/runner.rb16
-rw-r--r--app/models/commit_status.rb6
-rw-r--r--app/models/concerns/issuable.rb4
-rw-r--r--app/models/concerns/spammable.rb6
-rw-r--r--app/models/merge_requests_closing_issues.rb8
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/chat_message/base_message.rb4
-rw-r--r--app/models/project_services/chat_message/build_message.rb28
-rw-r--r--app/models/project_services/chat_message/issue_message.rb4
-rw-r--r--app/models/project_services/chat_message/merge_message.rb4
-rw-r--r--app/models/project_services/chat_message/note_message.rb9
-rw-r--r--app/models/project_services/drone_ci_service.rb2
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_snippet.rb4
-rw-r--r--app/models/user.rb1
-rw-r--r--app/serializers/analytics_stage_entity.rb1
-rw-r--r--app/services/base_service.rb5
-rw-r--r--app/services/ci/create_pipeline_service.rb3
-rw-r--r--app/services/ci/retry_build_service.rb42
-rw-r--r--app/services/ci/retry_pipeline_service.rb22
-rw-r--r--app/services/ci/update_runner_service.rb15
-rw-r--r--app/services/create_snippet_service.rb10
-rw-r--r--app/services/files/multi_service.rb8
-rw-r--r--app/services/issuable_base_service.rb32
-rw-r--r--app/services/issues/create_service.rb21
-rw-r--r--app/services/issues/update_service.rb8
-rw-r--r--app/services/merge_requests/add_todo_when_build_fails_service.rb6
-rw-r--r--app/services/merge_requests/build_service.rb26
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb3
-rw-r--r--app/services/projects/upload_service.rb2
-rw-r--r--app/services/spam_check_service.rb24
-rw-r--r--app/services/spam_service.rb31
-rw-r--r--app/services/todo_service.rb25
-rw-r--r--app/services/update_snippet_service.rb10
-rw-r--r--app/services/users/destroy_service.rb4
-rw-r--r--app/views/admin/abuse_reports/index.html.haml1
-rw-r--r--app/views/admin/application_settings/_form.html.haml4
-rw-r--r--app/views/admin/background_jobs/show.html.haml2
-rw-r--r--app/views/admin/runners/index.html.haml2
-rw-r--r--app/views/dashboard/todos/_todo.html.haml22
-rw-r--r--app/views/events/event/_push.html.haml8
-rw-r--r--app/views/groups/_head.html.haml19
-rw-r--r--app/views/groups/_head_issues.html.haml19
-rw-r--r--app/views/groups/activity.html.haml3
-rw-r--r--app/views/groups/group_members/index.html.haml1
-rw-r--r--app/views/groups/issues.html.haml1
-rw-r--r--app/views/groups/labels/index.html.haml1
-rw-r--r--app/views/groups/milestones/index.html.haml1
-rw-r--r--app/views/groups/show.html.haml1
-rw-r--r--app/views/layouts/_page.html.haml19
-rw-r--r--app/views/layouts/_recaptcha_verification.html.haml23
-rw-r--r--app/views/layouts/application.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml15
-rw-r--r--app/views/layouts/mailer.html.haml72
-rw-r--r--app/views/layouts/mailer.text.haml5
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml76
-rw-r--r--app/views/layouts/nav/_explore.html.haml2
-rw-r--r--app/views/layouts/nav/_group.html.haml20
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml280
-rw-r--r--app/views/notify/pipeline_failed_email.text.erb4
-rw-r--r--app/views/notify/pipeline_success_email.html.haml230
-rw-r--r--app/views/notify/pipeline_success_email.text.erb4
-rw-r--r--app/views/profiles/preferences/show.html.haml13
-rw-r--r--app/views/profiles/preferences/update.js.erb4
-rw-r--r--app/views/projects/blob/diff.html.haml2
-rw-r--r--app/views/projects/commit/_pipeline.html.haml2
-rw-r--r--app/views/projects/diffs/_content.html.haml7
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml7
-rw-r--r--app/views/projects/diffs/_text_file.html.haml7
-rw-r--r--app/views/projects/issues/show.html.haml4
-rw-r--r--app/views/projects/issues/verify.html.haml22
-rw-r--r--app/views/projects/merge_requests/index.html.haml7
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/notes/_note.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml6
-rw-r--r--app/views/projects/pipelines_settings/_badge.html.haml7
-rw-r--r--app/views/projects/pipelines_settings/_show.html.haml2
-rw-r--r--app/views/projects/snippets/_actions.html.haml4
-rw-r--r--app/views/projects/snippets/verify.html.haml4
-rw-r--r--app/views/projects/variables/_form.html.haml2
-rw-r--r--app/views/search/_results.html.haml2
-rw-r--r--app/views/search/results/_snippet_blob.html.haml55
-rw-r--r--app/views/shared/_issuable_meta_data.html.haml6
-rw-r--r--app/views/shared/_label.html.haml10
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml8
-rw-r--r--app/views/shared/members/_member.html.haml2
-rw-r--r--app/views/shared/milestones/_issuables.html.haml6
-rw-r--r--app/views/snippets/_actions.html.haml4
-rw-r--r--app/views/snippets/verify.html.haml4
-rw-r--r--app/workers/delete_user_worker.rb2
286 files changed, 1751 insertions, 1830 deletions
diff --git a/app/assets/images/favicon-blue.ico b/app/assets/images/favicon-blue.ico
index 71acdf670ab..156fcf07588 100644..100755
--- a/app/assets/images/favicon-blue.ico
+++ b/app/assets/images/favicon-blue.ico
Binary files differ
diff --git a/app/assets/images/icon-merge-request-unmerged.svg b/app/assets/images/icon-merge-request-unmerged.svg
new file mode 100644
index 00000000000..c4d8e65122d
--- /dev/null
+++ b/app/assets/images/icon-merge-request-unmerged.svg
@@ -0,0 +1 @@
+<svg width="12" height="15" viewBox="0 0 12 15" xmlns="http://www.w3.org/2000/svg"><path d="M10.267 11.028V5.167c-.028-.728-.318-1.372-.878-1.923-.56-.55-1.194-.85-1.922-.877h-.934V.5l-2.8 2.8 2.8 2.8V4.233h.934a.976.976 0 0 1 .644.29.88.88 0 0 1 .289.644v5.861a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472zM3.733 3.3a1.86 1.86 0 0 0-1.866-1.867 1.86 1.86 0 0 0-.934 3.472v6.123a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472V4.905c.55-.317.933-.914.933-1.605z" fill-rule="nonzero"/></svg>
diff --git a/app/assets/images/mailers/gitlab_footer_logo.gif b/app/assets/images/mailers/gitlab_footer_logo.gif
new file mode 100644
index 00000000000..3f4ef31947b
--- /dev/null
+++ b/app/assets/images/mailers/gitlab_footer_logo.gif
Binary files differ
diff --git a/app/assets/images/mailers/gitlab_header_logo.gif b/app/assets/images/mailers/gitlab_header_logo.gif
new file mode 100644
index 00000000000..387628f831c
--- /dev/null
+++ b/app/assets/images/mailers/gitlab_header_logo.gif
Binary files differ
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index 424dc719c78..aaed74d6073 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -61,4 +61,4 @@
return Admin;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 84bbe90f3b1..86e0ad89431 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -147,4 +147,4 @@
};
window.Api = Api;
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 4b5c9686cab..8e468faedbf 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -101,11 +101,6 @@ require('es6-promise').polyfill();
}
});
- $('.nav-sidebar').niceScroll({
- cursoropacitymax: '0.4',
- cursorcolor: '#FFF',
- cursorborder: '1px solid #FFF'
- });
$('.js-select-on-focus').on('focusin', function () {
return $(this).select().one('mouseup', function (e) {
return e.preventDefault();
@@ -245,9 +240,7 @@ require('es6-promise').polyfill();
});
gl.awardsHandler = new AwardsHandler();
new Aside();
- // bind sidebar events
- new gl.Sidebar();
gl.utils.initTimeagoTimeout();
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js
index 8438de6cdf1..448e6e2cc78 100644
--- a/app/assets/javascripts/aside.js
+++ b/app/assets/javascripts/aside.js
@@ -22,4 +22,4 @@
return Aside;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index b16a2c0f73a..e55405135fb 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -59,4 +59,4 @@
return Autosave;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 9d776b74965..a4ccb30e447 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -377,4 +377,4 @@ var emojiAliases = require('emoji-aliases');
return AwardsHandler;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js
index a489523b802..f7f41d55b52 100644
--- a/app/assets/javascripts/behaviors/autosize.js
+++ b/app/assets/javascripts/behaviors/autosize.js
@@ -25,4 +25,4 @@ var autosize = require('vendor/autosize');
autosize.update($fields);
return $fields.css('resize', 'vertical');
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js
index 6af8f593872..fd0840fa117 100644
--- a/app/assets/javascripts/behaviors/details_behavior.js
+++ b/app/assets/javascripts/behaviors/details_behavior.js
@@ -23,4 +23,4 @@
return e.preventDefault();
});
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index 7747306688c..a7e68ae5cb9 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -74,4 +74,4 @@ require('../extensions/jquery');
return $this.tooltip('hide');
});
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index 6276933e93e..6b21695d082 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -59,4 +59,4 @@ require('../extensions/jquery');
return hideOrShowHelpBlock($form);
});
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index 04bfe363929..5f14ff40eee 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -63,4 +63,4 @@
return BlobFileDropzone;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js b/app/assets/javascripts/blob/blob_gitignore_selector.js
index 1d0bcf6471f..de20eab9cd1 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selector.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selector.js
@@ -20,4 +20,4 @@ require('./template_selector');
return BlobGitignoreSelector;
})(gl.TemplateSelector);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js
index 8236457f0f1..43e5c0a5641 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selectors.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selectors.js
@@ -23,4 +23,4 @@
return BlobGitignoreSelectors;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/blob/blob_license_selector.js b/app/assets/javascripts/blob/blob_license_selector.js
index 1d5672d4c48..b582052a76e 100644
--- a/app/assets/javascripts/blob/blob_license_selector.js
+++ b/app/assets/javascripts/blob/blob_license_selector.js
@@ -25,4 +25,4 @@ require('./template_selector');
return BlobLicenseSelector;
})(gl.TemplateSelector);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
index 9e0754819fa..0436bbb0eaf 100644
--- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
@@ -12,4 +12,4 @@ require('./edit_blob');
var blob = new EditBlob(url, $('.js-edit-blob-form').data('blob-language'));
new NewCommitForm($('.js-edit-blob-form'));
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 079445e8278..a1127b9e30e 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -85,4 +85,4 @@
return EditBlob;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6
index 8f30900198e..55d13be6e5f 100644
--- a/app/assets/javascripts/boards/boards_bundle.js.es6
+++ b/app/assets/javascripts/boards/boards_bundle.js.es6
@@ -1,16 +1,20 @@
-/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren, import/newline-after-import, no-multi-spaces, max-len */
+/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
/* global Vue */
/* global BoardService */
-function requireAll(context) { return context.keys().map(context); }
-
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
-requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./mixins', true, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./filters', true, /^\.\/.*\.(js|es6)$/));
+require('./models/issue');
+require('./models/label');
+require('./models/list');
+require('./models/milestone');
+require('./models/user');
+require('./stores/boards_store');
+require('./stores/modal_store');
+require('./services/board_service');
+require('./mixins/modal_mixins');
+require('./mixins/sortable_default_options');
+require('./filters/due_date_filters');
require('./components/board');
require('./components/board_sidebar');
require('./components/new_list_dropdown');
@@ -93,17 +97,53 @@ $(() => {
modal: ModalStore.store,
store: Store.state,
},
+ watch: {
+ disabled() {
+ this.updateTooltip();
+ },
+ },
computed: {
disabled() {
- return Store.shouldAddBlankState();
+ return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length;
+ },
+ tooltipTitle() {
+ if (this.disabled) {
+ return 'Please add a list to your board first';
+ }
+
+ return '';
},
},
+ methods: {
+ updateTooltip() {
+ const $tooltip = $(this.$el);
+
+ this.$nextTick(() => {
+ if (this.disabled) {
+ $tooltip.tooltip();
+ } else {
+ $tooltip.tooltip('destroy');
+ }
+ });
+ },
+ openModal() {
+ if (!this.disabled) {
+ this.toggleModal(true);
+ }
+ },
+ },
+ mounted() {
+ this.updateTooltip();
+ },
template: `
<button
- class="btn btn-create pull-right prepend-left-10 has-tooltip"
+ class="btn btn-create pull-right prepend-left-10"
type="button"
- :disabled="disabled"
- @click="toggleModal(true)">
+ data-placement="bottom"
+ :class="{ 'disabled': disabled }"
+ :title="tooltipTitle"
+ :aria-disabled="disabled"
+ @click="openModal">
Add issues
</button>
`,
diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js
index f8dac1ff56e..22e93328548 100644
--- a/app/assets/javascripts/breakpoints.js
+++ b/app/assets/javascripts/breakpoints.js
@@ -69,4 +69,4 @@
})(this));
window.Breakpoints = Breakpoints;
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/broadcast_message.js b/app/assets/javascripts/broadcast_message.js
index dbdadc73c3f..e8531c43b4b 100644
--- a/app/assets/javascripts/broadcast_message.js
+++ b/app/assets/javascripts/broadcast_message.js
@@ -31,4 +31,4 @@
}
});
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index c5a962dd199..8fa1aceddff 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -275,4 +275,4 @@
return Build;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js
index 083448552b6..cae9a0ffca4 100644
--- a/app/assets/javascripts/build_artifacts.js
+++ b/app/assets/javascripts/build_artifacts.js
@@ -23,4 +23,4 @@
return BuildArtifacts;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js
index c656ae4e241..566b322eb49 100644
--- a/app/assets/javascripts/commit.js
+++ b/app/assets/javascripts/commit.js
@@ -11,4 +11,4 @@
return Commit;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js
index 184b4561d2e..ee087c978dd 100644
--- a/app/assets/javascripts/commit/file.js
+++ b/app/assets/javascripts/commit/file.js
@@ -11,4 +11,4 @@
return CommitFile;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index f09a6b1e676..49bb64a3472 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -173,4 +173,4 @@
return ImageFile;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index c6fdfbcaa10..ccd895f3bf4 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -65,4 +65,4 @@
return CommitsList;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index 9591df70e9c..15df105d4cc 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -88,4 +88,4 @@
return Compare;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/compare_autocomplete.js.es6 b/app/assets/javascripts/compare_autocomplete.js.es6
index 3587431ab69..1eca973e069 100644
--- a/app/assets/javascripts/compare_autocomplete.js.es6
+++ b/app/assets/javascripts/compare_autocomplete.js.es6
@@ -66,4 +66,4 @@
return CompareAutocomplete;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 35d98492012..a1c1b721228 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -28,4 +28,4 @@
return ConfirmDangerModal;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js
index 0029c59e550..615f485e18a 100644
--- a/app/assets/javascripts/copy_to_clipboard.js
+++ b/app/assets/javascripts/copy_to_clipboard.js
@@ -46,4 +46,4 @@ window.Clipboard = require('vendor/clipboard');
clipboard.on('success', genericSuccess);
return clipboard.on('error', genericError);
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/create_label.js.es6 b/app/assets/javascripts/create_label.js.es6
index 947c129d5b5..85384d98126 100644
--- a/app/assets/javascripts/create_label.js.es6
+++ b/app/assets/javascripts/create_label.js.es6
@@ -107,9 +107,9 @@
if (typeof label.message === 'string') {
errors = label.message;
} else {
- errors = label.message.map(function (value, key) {
- return key + " " + value[0];
- }).join("<br/>");
+ errors = Object.keys(label.message).map(key =>
+ `${gl.text.humanize(key)} ${label.message[key].join(', ')}`
+ ).join("<br/>");
}
this.$newLabelError
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
index dbdb01c8c68..411ac7b24b2 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
@@ -4,10 +4,20 @@
window.Vue = require('vue');
window.Cookies = require('js-cookie');
-
-function requireAll(context) { return context.keys().map(context); }
-requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('.', true, /^\.\/(?!cycle_analytics_bundle).*\.(js|es6)$/));
+require('./svg/icon_branch');
+require('./svg/icon_build_status');
+require('./svg/icon_commit');
+require('./components/stage_code_component');
+require('./components/stage_issue_component');
+require('./components/stage_plan_component');
+require('./components/stage_production_component');
+require('./components/stage_review_component');
+require('./components/stage_staging_component');
+require('./components/stage_test_component');
+require('./components/total_time_component');
+require('./cycle_analytics_service');
+require('./cycle_analytics_store');
+require('./default_event_objects');
$(() => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
@@ -97,7 +107,7 @@ $(() => {
}
this.isLoadingStage = true;
- cycleAnalyticsStore.setStageEvents([]);
+ cycleAnalyticsStore.setStageEvents([], stage);
cycleAnalyticsStore.setActiveStage(stage);
cycleAnalyticsService
@@ -107,7 +117,7 @@ $(() => {
})
.done((response) => {
this.isEmptyStage = !response.events.length;
- cycleAnalyticsStore.setStageEvents(response.events);
+ cycleAnalyticsStore.setStageEvents(response.events, stage);
})
.error(() => {
this.isEmptyStage = true;
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
index be732971c7f..3efeb141008 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
@@ -1,4 +1,8 @@
/* eslint-disable no-param-reassign */
+
+require('../lib/utils/text_utility');
+const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
+
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
@@ -34,11 +38,12 @@
});
newData.stages.forEach((item) => {
- const stageName = item.title.toLowerCase();
+ const stageSlug = gl.text.dasherize(item.title.toLowerCase());
item.active = false;
- item.isUserAllowed = data.permissions[stageName];
- item.emptyStageText = EMPTY_STAGE_TEXTS[stageName];
- item.component = `stage-${stageName}-component`;
+ item.isUserAllowed = data.permissions[stageSlug];
+ item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug];
+ item.component = `stage-${stageSlug}-component`;
+ item.slug = stageSlug;
});
newData.analytics = data;
return newData;
@@ -58,31 +63,33 @@
this.deactivateAllStages();
stage.active = true;
},
- setStageEvents(events) {
- this.state.events = this.decorateEvents(events);
+ setStageEvents(events, stage) {
+ this.state.events = this.decorateEvents(events, stage);
},
- decorateEvents(events) {
+ decorateEvents(events, stage) {
const newEvents = [];
events.forEach((item) => {
if (!item) return;
- item.totalTime = item.total_time;
- item.author.webUrl = item.author.web_url;
- item.author.avatarUrl = item.author.avatar_url;
+ const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
+
+ eventItem.totalTime = eventItem.total_time;
+ eventItem.author.webUrl = eventItem.author.web_url;
+ eventItem.author.avatarUrl = eventItem.author.avatar_url;
- if (item.created_at) item.createdAt = item.created_at;
- if (item.short_sha) item.shortSha = item.short_sha;
- if (item.commit_url) item.commitUrl = item.commit_url;
+ if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
+ if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
+ if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url;
- delete item.author.web_url;
- delete item.author.avatar_url;
- delete item.total_time;
- delete item.created_at;
- delete item.short_sha;
- delete item.commit_url;
+ delete eventItem.author.web_url;
+ delete eventItem.author.avatar_url;
+ delete eventItem.total_time;
+ delete eventItem.created_at;
+ delete eventItem.short_sha;
+ delete eventItem.commit_url;
- newEvents.push(item);
+ newEvents.push(eventItem);
});
return newEvents;
diff --git a/app/assets/javascripts/cycle_analytics/default_event_objects.js.es6 b/app/assets/javascripts/cycle_analytics/default_event_objects.js.es6
new file mode 100644
index 00000000000..cfaf9835bf8
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/default_event_objects.js.es6
@@ -0,0 +1,98 @@
+module.exports = {
+ issue: {
+ created_at: '',
+ url: '',
+ iid: '',
+ title: '',
+ total_time: {},
+ author: {
+ avatar_url: '',
+ id: '',
+ name: '',
+ web_url: '',
+ },
+ },
+ plan: {
+ title: '',
+ commit_url: '',
+ short_sha: '',
+ total_time: {},
+ author: {
+ name: '',
+ id: '',
+ avatar_url: '',
+ web_url: '',
+ },
+ },
+ code: {
+ title: '',
+ iid: '',
+ created_at: '',
+ url: '',
+ total_time: {},
+ author: {
+ name: '',
+ id: '',
+ avatar_url: '',
+ web_url: '',
+ },
+ },
+ test: {
+ name: '',
+ id: '',
+ date: '',
+ url: '',
+ short_sha: '',
+ commit_url: '',
+ total_time: {},
+ branch: {
+ name: '',
+ url: '',
+ },
+ },
+ review: {
+ title: '',
+ iid: '',
+ created_at: '',
+ url: '',
+ state: '',
+ total_time: {},
+ author: {
+ name: '',
+ id: '',
+ avatar_url: '',
+ web_url: '',
+ },
+ },
+ staging: {
+ id: '',
+ short_sha: '',
+ date: '',
+ url: '',
+ commit_url: '',
+ total_time: {},
+ author: {
+ name: '',
+ id: '',
+ avatar_url: '',
+ web_url: '',
+ },
+ branch: {
+ name: '',
+ url: '',
+ },
+ },
+ production: {
+ title: '',
+ created_at: '',
+ url: '',
+ iid: '',
+ total_time: {},
+ author: {
+ name: '',
+ id: '',
+ avatar_url: '',
+ web_url: '',
+ },
+ },
+};
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
index 190461451d5..cadf8b96b87 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
@@ -1,14 +1,18 @@
-/* eslint-disable func-names, comma-dangle, new-cap, no-new, import/newline-after-import, no-multi-spaces, max-len */
+/* eslint-disable func-names, comma-dangle, new-cap, no-new, max-len */
/* global Vue */
/* global ResolveCount */
-function requireAll(context) { return context.keys().map(context); }
const Vue = require('vue');
-requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/));
-requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/));
+require('./models/discussion');
+require('./models/note');
+require('./stores/comments');
+require('./services/resolve');
+require('./mixins/discussion');
+require('./components/comment_resolve_btn');
+require('./components/jump_to_discussion');
+require('./components/resolve_btn');
+require('./components/resolve_count');
+require('./components/resolve_discussion_btn');
$(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 7eec2d39a9c..f55db02f0fd 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -74,7 +74,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:merge_requests:index':
case 'projects:issues:index':
if (gl.FilteredSearchManager) {
- new gl.FilteredSearchManager();
+ new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
}
Issuable.init();
new gl.IssuableBulkActions({
@@ -118,6 +118,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
new gl.IssuableTemplateSelectors();
break;
case 'projects:merge_requests:new':
+ case 'projects:merge_requests:new_diffs':
case 'projects:merge_requests:edit':
new gl.Diff();
shortcut_handler = new ShortcutsNavigation();
@@ -382,4 +383,4 @@ const ShortcutsBlob = require('./shortcuts_blob');
return Dispatcher;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index a510eebae1a..646f836aff0 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -126,13 +126,14 @@ require('./preview_markdown');
};
pasteText = function(text) {
var afterSelection, beforeSelection, caretEnd, caretStart, textEnd;
+ var formattedText = text + "\n\n";
caretStart = $(child)[0].selectionStart;
caretEnd = $(child)[0].selectionEnd;
textEnd = $(child).val().length;
beforeSelection = $(child).val().substring(0, caretStart);
afterSelection = $(child).val().substring(caretEnd, textEnd);
- $(child).val(beforeSelection + text + afterSelection);
- child.get(0).setSelectionRange(caretStart + text.length, caretEnd + text.length);
+ $(child).val(beforeSelection + formattedText + afterSelection);
+ child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
return form_textarea.trigger("input");
};
getFilename = function(e) {
@@ -216,4 +217,4 @@ require('./preview_markdown');
return DropzoneInput;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6
index 0cbf952ea5c..4b700a39d44 100644
--- a/app/assets/javascripts/environments/components/environment.js.es6
+++ b/app/assets/javascripts/environments/components/environment.js.es6
@@ -1,13 +1,14 @@
/* eslint-disable no-param-reassign, no-new */
/* global Flash */
-const Vue = require('vue');
-Vue.use(require('vue-resource'));
+const Vue = window.Vue = require('vue');
+window.Vue.use(require('vue-resource'));
const EnvironmentsService = require('../services/environments_service');
const EnvironmentTable = require('./environments_table');
const EnvironmentsStore = require('../stores/environments_store');
require('../../vue_shared/components/table_pagination');
require('../../lib/utils/common_utils');
+require('../../vue_shared/vue_resource_interceptor');
module.exports = Vue.component('environment-component', {
diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6
index c5a714d9673..978d4dd8b6b 100644
--- a/app/assets/javascripts/environments/components/environment_actions.js.es6
+++ b/app/assets/javascripts/environments/components/environment_actions.js.es6
@@ -15,29 +15,29 @@ module.exports = Vue.component('actions-component', {
},
template: `
- <div class="inline">
- <div class="dropdown">
- <a class="dropdown-new btn btn-default" data-toggle="dropdown">
+ <div class="btn-group" role="group">
+ <button class="dropdown btn btn-default dropdown-new" data-toggle="dropdown">
+ <span>
<span class="js-dropdown-play-icon-container" v-html="playIconSvg"></span>
<i class="fa fa-caret-down"></i>
- </a>
+ </span>
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="action in actions">
- <a :href="action.play_path"
- data-method="post"
- rel="nofollow"
- class="js-manual-action-link">
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="action in actions">
+ <a :href="action.play_path"
+ data-method="post"
+ rel="nofollow"
+ class="js-manual-action-link">
- <span class="js-action-play-icon-container" v-html="playIconSvg"></span>
+ <span class="js-action-play-icon-container" v-html="playIconSvg"></span>
- <span>
- {{action.name}}
- </span>
- </a>
- </li>
- </ul>
- </div>
- </div>
+ <span>
+ {{action.name}}
+ </span>
+ </a>
+ </li>
+ </ul>
+ </button>
+ </div>
`,
});
diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6
index 24fd58a301a..ad9d1d21a79 100644
--- a/app/assets/javascripts/environments/components/environment_item.js.es6
+++ b/app/assets/javascripts/environments/components/environment_item.js.es6
@@ -505,39 +505,26 @@ module.exports = Vue.component('environment-item', {
<td class="hidden-xs">
<div v-if="!model.isFolder">
- <div v-if="hasManualActions && canCreateDeployment"
- class="inline js-manual-actions-container">
- <actions-component
+ <div class="btn-group" role="group">
+ <actions-component v-if="hasManualActions && canCreateDeployment"
:play-icon-svg="playIconSvg"
:actions="manualActions">
</actions-component>
- </div>
- <div v-if="externalURL && canReadEnvironment"
- class="inline js-external-url-container">
- <external-url-component
+ <external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL">
</external-url-component>
- </div>
- <div v-if="hasStopAction && canCreateDeployment"
- class="inline js-stop-component-container">
- <stop-component
+ <stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path">
</stop-component>
- </div>
- <div v-if="model && model.terminal_path"
- class="inline js-terminal-button-container">
- <terminal-button-component
+ <terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg"
:terminal-path="model.terminal_path">
</terminal-button-component>
- </div>
- <div v-if="canRetry && canCreateDeployment"
- class="inline js-rollback-component-container">
- <rollback-component
+ <rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl">
</rollback-component>
diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6
index 867eba1d384..7bbba91bc10 100644
--- a/app/assets/javascripts/environments/environments_bundle.js.es6
+++ b/app/assets/javascripts/environments/environments_bundle.js.es6
@@ -1,5 +1,4 @@
const EnvironmentsComponent = require('./components/environment');
-require('../vue_shared/vue_resource_interceptor');
$(() => {
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6
index 29f704c1a37..d2ca465351a 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6
@@ -1,5 +1,4 @@
const EnvironmentsFolderComponent = require('./environments_folder_view');
-require('../../vue_shared/vue_resource_interceptor');
$(() => {
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6
index 0b1204559da..53d52965758 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6
@@ -1,13 +1,14 @@
/* eslint-disable no-param-reassign, no-new */
/* global Flash */
-const Vue = require('vue');
-Vue.use(require('vue-resource'));
+const Vue = window.Vue = require('vue');
+window.Vue.use(require('vue-resource'));
const EnvironmentsService = require('../services/environments_service');
const EnvironmentTable = require('../components/environments_table');
const EnvironmentsStore = require('../stores/environments_store');
require('../../vue_shared/components/table_pagination');
require('../../lib/utils/common_utils');
+require('../../vue_shared/vue_resource_interceptor');
module.exports = Vue.component('environment-folder-view', {
diff --git a/app/assets/javascripts/extensions/jquery.js b/app/assets/javascripts/extensions/jquery.js
index d3b58b2707a..1a489b859e8 100644
--- a/app/assets/javascripts/extensions/jquery.js
+++ b/app/assets/javascripts/extensions/jquery.js
@@ -13,4 +13,4 @@
return $(this).removeAttr('disabled').removeClass('disabled');
}
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 895a872568d..698870d0ce1 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -144,4 +144,4 @@
}
});
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
index 572c221929a..9e92d544bef 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
@@ -37,23 +37,18 @@ require('./filtered_search_dropdown');
}
renderContent() {
- const dropdownData = [{
- icon: 'fa-pencil',
- hint: 'author:',
- tag: '&lt;@author&gt;',
- }, {
- icon: 'fa-user',
- hint: 'assignee:',
- tag: '&lt;@assignee&gt;',
- }, {
- icon: 'fa-clock-o',
- hint: 'milestone:',
- tag: '&lt;%milestone&gt;',
- }, {
- icon: 'fa-tag',
- hint: 'label:',
- tag: '&lt;~label&gt;',
- }];
+ const dropdownData = [];
+
+ [].forEach.call(this.input.parentElement.querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
+ const { icon, hint, tag } = dropdownMenu.dataset;
+ if (icon && hint && tag) {
+ dropdownData.push({
+ icon: `fa-${icon}`,
+ hint,
+ tag: `&lt;${tag}&gt;`,
+ });
+ }
+ });
this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
this.droplab.setData(this.hookId, dropdownData);
diff --git a/app/assets/javascripts/filtered_search/filtered_search_bundle.js b/app/assets/javascripts/filtered_search/filtered_search_bundle.js
index 392f1835966..faaba994f46 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_bundle.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_bundle.js
@@ -1,3 +1,9 @@
-function requireAll(context) { return context.keys().map(context); }
-
-requireAll(require.context('./', true, /^\.\/(?!filtered_search_bundle).*\.(js|es6)$/));
+require('./dropdown_hint');
+require('./dropdown_non_user');
+require('./dropdown_user');
+require('./dropdown_utils');
+require('./filtered_search_dropdown_manager');
+require('./filtered_search_dropdown');
+require('./filtered_search_manager');
+require('./filtered_search_token_keys');
+require('./filtered_search_tokenizer');
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
index e8c2df03a46..fbc72a3001a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
@@ -52,8 +52,9 @@
}
renderContent(forceShowList = false) {
- if (forceShowList && this.getCurrentHook().list.hidden) {
- this.getCurrentHook().list.show();
+ const currentHook = this.getCurrentHook();
+ if (forceShowList && currentHook && currentHook.list.hidden) {
+ currentHook.list.show();
}
}
@@ -92,18 +93,24 @@
}
hideDropdown() {
- this.getCurrentHook().list.hide();
+ const currentHook = this.getCurrentHook();
+ if (currentHook) {
+ currentHook.list.hide();
+ }
}
resetFilters() {
const hook = this.getCurrentHook();
- const data = hook.list.data;
- const results = data.map((o) => {
- const updated = o;
- updated.droplab_hidden = false;
- return updated;
- });
- hook.list.render(results);
+
+ if (hook) {
+ const data = hook.list.data;
+ const results = data.map((o) => {
+ const updated = o;
+ updated.droplab_hidden = false;
+ return updated;
+ });
+ hook.list.render(results);
+ }
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
index 8ce4cf4fc36..cecd3518ce3 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
@@ -2,10 +2,12 @@
(() => {
class FilteredSearchDropdownManager {
- constructor(baseEndpoint = '') {
+ constructor(baseEndpoint = '', page) {
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
+ this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = document.querySelector('.filtered-search');
+ this.page = page;
this.setupMapping();
@@ -150,7 +152,7 @@
this.droplab = new DropLab();
}
- const match = gl.FilteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
+ const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
index ffc7d29e4c5..bbafead0305 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
@@ -1,12 +1,13 @@
(() => {
class FilteredSearchManager {
- constructor() {
+ constructor(page) {
this.filteredSearchInput = document.querySelector('.filtered-search');
this.clearSearchButton = document.querySelector('.clear-search');
+ this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer;
- this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '');
+ this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
this.bindEvents();
this.loadSearchParamsFromURL();
@@ -117,8 +118,8 @@
const keyParam = decodeURIComponent(split[0]);
const value = split[1];
- // Check if it matches edge conditions listed in gl.FilteredSearchTokenKeys
- const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(p);
+ // Check if it matches edge conditions listed in this.filteredSearchTokenKeys
+ const condition = this.filteredSearchTokenKeys.searchByConditionUrl(p);
if (condition) {
inputValues.push(`${condition.tokenKey}:${condition.value}`);
@@ -126,7 +127,7 @@
// Sanitize value since URL converts spaces into +
// Replace before decode so that we know what was originally + versus the encoded +
const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value;
- const match = gl.FilteredSearchTokenKeys.searchByKeyParam(keyParam);
+ const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
if (match) {
const indexOf = keyParam.indexOf('_');
@@ -171,9 +172,9 @@
paths.push(`state=${currentState}`);
tokens.forEach((token) => {
- const condition = gl.FilteredSearchTokenKeys
+ const condition = this.filteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
- const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key);
+ const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = '';
diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
index cf53845a48b..9bf1b1ced88 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
@@ -1,9 +1,12 @@
+require('./filtered_search_token_keys');
+
(() => {
class FilteredSearchTokenizer {
static processTokens(input) {
+ const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
- const tokenRegex = /(\w+):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\S+))/g;
+ const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = [];
let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index 249fe23d4cb..730104b89f9 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -39,4 +39,4 @@
return Flash;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 7f1f2a5d278..60d6658dc16 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -83,12 +83,12 @@
_a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF");
- regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])(([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi');
+ regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?!" + atSymbolsWithBar + ")((?:[A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi');
match = regexp.exec(subtext);
if (match) {
- return (match[1] || match[1] === "") ? match[1] : match[2];
+ return match[1];
} else {
return null;
}
@@ -103,6 +103,9 @@
this.input.each((i, input) => {
const $input = $(input);
$input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input));
+ // This triggers at.js again
+ // Needed for slash commands with suffixes (ex: /label ~)
+ $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup'));
});
},
setupAtWho: function($input) {
@@ -377,4 +380,4 @@
(dataToInspect === loadingState || dataToInspect.name === loadingState);
}
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 0d618caf350..a01662e2f9e 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -47,9 +47,10 @@
}
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
- $inputContainer.parent().addClass('is-loading');
clearTimeout(timeout);
return timeout = setTimeout(function() {
+ $inputContainer.parent().addClass('is-loading');
+
return this.options.query(this.input.val(), function(data) {
$inputContainer.parent().removeClass('is-loading');
return this.options.callback(data);
@@ -846,4 +847,4 @@
}
});
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index 4f7777aa5bc..086dcb34571 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,3 +1,4 @@
-// require everything else in this directory
-function requireAll(context) { return context.keys().map(context); }
-requireAll(require.context('.', false, /^\.\/(?!graphs_bundle).*\.(js|es6)$/));
+require('./stat_graph_contributors_graph');
+require('./stat_graph_contributors_util');
+require('./stat_graph_contributors');
+require('./stat_graph');
diff --git a/app/assets/javascripts/graphs/stat_graph.js b/app/assets/javascripts/graphs/stat_graph.js
index 2e6da5750de..75a53aae33c 100644
--- a/app/assets/javascripts/graphs/stat_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph.js
@@ -15,4 +15,4 @@
return StatGraph;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js
index d06a1a5dae4..bbfb467ad50 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js
@@ -113,4 +113,4 @@ window.d3 = require('d3');
return ContributorsStatGraph;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
index 241249fae63..228771da4ee 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -273,4 +273,4 @@ window.d3 = require('d3');
return ContributorsAuthorGraph;
})(ContributorsGraph);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
index 29c3163328f..7954c583598 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
@@ -135,4 +135,4 @@
}
}
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js
index 10dfd05fe3c..c5cb273c5b2 100644
--- a/app/assets/javascripts/group_avatar.js
+++ b/app/assets/javascripts/group_avatar.js
@@ -17,4 +17,4 @@
return GroupAvatar;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index bc88dc2d092..6b937e7fa0f 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -68,4 +68,4 @@
return GroupsSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index fa85f9a6c86..a853c3aeb1f 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -2,7 +2,7 @@
(function() {
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
- $todoPendingCount.text(gl.text.addDelimiter(count));
+ $todoPendingCount.text(gl.text.highCountTrim(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
})();
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 9390136d3d8..34e4a257ff9 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -78,4 +78,4 @@
new window.ImporterStatus(jobsImportPath, importPath);
}
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js
index c77fbb6a1c7..115312d4b83 100644
--- a/app/assets/javascripts/issuable_context.js
+++ b/app/assets/javascripts/issuable_context.js
@@ -76,4 +76,4 @@
return IssuableContext;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index c7c744ef61f..de184ab2675 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -156,4 +156,4 @@
return IssuableForm;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 1776b3d61f6..52457f70d90 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -3,7 +3,7 @@
require('./flash');
require('vendor/jquery.waitforimages');
-require('vendor/task_list');
+require('./task_list');
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@@ -11,10 +11,16 @@ require('vendor/task_list');
this.Issue = (function() {
function Issue() {
this.submitNoteForm = bind(this.submitNoteForm, this);
- // Prevent duplicate event bindings
- this.disableTaskList();
if ($('a.btn-close').length) {
- this.initTaskList();
+ this.taskList = new gl.TaskList({
+ dataType: 'issue',
+ fieldName: 'description',
+ selector: '.detail-page-description',
+ onSuccess: (result) => {
+ document.querySelector('#task_status').innerText = result.task_status;
+ document.querySelector('#task_status_short').innerText = result.task_status_short;
+ }
+ });
this.initIssueBtnEventListeners();
}
this.initMergeRequests();
@@ -22,11 +28,6 @@ require('vendor/task_list');
this.initCanCreateBranch();
}
- Issue.prototype.initTaskList = function() {
- $('.detail-page-description .js-task-list-container').taskList('enable');
- return $(document).on('tasklist:changed', '.detail-page-description .js-task-list-container', this.updateTaskList);
- };
-
Issue.prototype.initIssueBtnEventListeners = function() {
var _this, issueFailMessage;
_this = this;
@@ -85,30 +86,6 @@ require('vendor/task_list');
}
};
- Issue.prototype.disableTaskList = function() {
- $('.detail-page-description .js-task-list-container').taskList('disable');
- return $(document).off('tasklist:changed', '.detail-page-description .js-task-list-container');
- };
-
- Issue.prototype.updateTaskList = function() {
- var patchData;
- patchData = {};
- patchData['issue'] = {
- 'description': $('.js-task-list-field', this).val()
- };
- return $.ajax({
- type: 'PATCH',
- url: $('form.js-issuable-update').attr('action'),
- data: patchData,
- success: function(issue) {
- document.querySelector('#task_status').innerText = issue.task_status;
- document.querySelector('#task_status_short').innerText = issue.task_status_short;
- }
- });
- // TODO (rspeicher): Make the issue description inline-editable like a note so
- // that we can re-use its form here
- };
-
Issue.prototype.initMergeRequests = function() {
var $container;
$container = $('#merge-requests');
@@ -155,4 +132,4 @@ require('vendor/task_list');
return Issue;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js
index 1d6eff11403..b2cfd3ef2a3 100644
--- a/app/assets/javascripts/issue_status_select.js
+++ b/app/assets/javascripts/issue_status_select.js
@@ -31,4 +31,4 @@
return IssueStatusSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js
index 40ad6fc348e..17a3fc1b1e4 100644
--- a/app/assets/javascripts/labels.js
+++ b/app/assets/javascripts/labels.js
@@ -43,4 +43,4 @@
return Labels;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index e4cf9057e6d..9e2d14c7f87 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -504,4 +504,4 @@
return LabelsSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index 1c0ea317c1a..08ca9e4fa4d 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -44,4 +44,4 @@
}
});
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/cropper.js b/app/assets/javascripts/lib/cropper.js
index 5221f85ba7a..7862c6797c3 100644
--- a/app/assets/javascripts/lib/cropper.js
+++ b/app/assets/javascripts/lib/cropper.js
@@ -4,4 +4,4 @@
(function() {
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/raphael.js b/app/assets/javascripts/lib/raphael.js
index 5a9a501efe3..ebe1e2ae98d 100644
--- a/app/assets/javascripts/lib/raphael.js
+++ b/app/assets/javascripts/lib/raphael.js
@@ -6,4 +6,4 @@
(function() {
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js
index ce090a2e4fd..d93c1d0da59 100644
--- a/app/assets/javascripts/lib/utils/animate.js
+++ b/app/assets/javascripts/lib/utils/animate.js
@@ -46,4 +46,4 @@
return dfd.promise();
};
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6
index 764aff51fee..45a1d90a9d9 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js.es6
+++ b/app/assets/javascripts/lib/utils/common_utils.js.es6
@@ -297,4 +297,4 @@
*/
w.gl.utils.convertPermissionToBoolean = permission => permission === 'true';
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js.es6 b/app/assets/javascripts/lib/utils/datetime_utility.js.es6
index f41fa15b147..82dcbdc26c8 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js.es6
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js.es6
@@ -123,4 +123,4 @@ window.dateFormat = require('vendor/date.format');
return Math.floor((date2 - date1) / millisecondsPerDay);
};
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index 6d5979603b9..66f39122a66 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -44,4 +44,4 @@
w.notify = notifyMe;
return w.notifyPermissions = notifyPermissions;
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index d9370db0cf2..579d322e3fb 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -1,5 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len */
+require('vendor/latinise');
+
(function() {
(function(w) {
var base;
@@ -12,6 +14,9 @@
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
};
+ gl.text.highCountTrim = function(count) {
+ return count > 99 ? '99+' : count;
+ };
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
@@ -164,8 +169,14 @@
gl.text.pluralize = function(str, count) {
return str + (count > 1 || count === 0 ? 's' : '');
};
- return gl.text.truncate = function(string, maxLength) {
+ gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...';
};
+ gl.text.dasherize = function(str) {
+ return str.replace(/[_\s]+/g, '-');
+ };
+ gl.text.slugify = function(str) {
+ return str.trim().toLowerCase().latinise();
+ };
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/type_utility.js b/app/assets/javascripts/lib/utils/type_utility.js
index 6d813d61601..db62e0be324 100644
--- a/app/assets/javascripts/lib/utils/type_utility.js
+++ b/app/assets/javascripts/lib/utils/type_utility.js
@@ -12,4 +12,4 @@
return (obj != null) && (obj.constructor === Object);
};
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/lib/utils/url_utility.js.es6 b/app/assets/javascripts/lib/utils/url_utility.js.es6
index a1558b371f0..1bc81d2e4a4 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js.es6
+++ b/app/assets/javascripts/lib/utils/url_utility.js.es6
@@ -83,4 +83,4 @@
document.location.href = url;
};
})(window);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index d7137ec63e4..966fcd8ec47 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -179,4 +179,4 @@ require('vendor/jquery.scrollTo');
return LineHighlighter;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js
index 1b0d0768db8..729baa2e1a7 100644
--- a/app/assets/javascripts/logo.js
+++ b/app/assets/javascripts/logo.js
@@ -4,4 +4,4 @@
window.addEventListener('beforeunload', function() {
$('.tanuki-logo').addClass('animate');
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/member_expiration_date.js.es6 b/app/assets/javascripts/member_expiration_date.js.es6
index efe7c78a8ec..129d2dc5f0a 100644
--- a/app/assets/javascripts/member_expiration_date.js.es6
+++ b/app/assets/javascripts/member_expiration_date.js.es6
@@ -49,4 +49,4 @@
inputs.each(toggleClearInput);
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index e65378cd610..5e01aacf2ba 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -2,7 +2,7 @@
/* global MergeRequestTabs */
require('vendor/jquery.waitforimages');
-require('vendor/task_list');
+require('./task_list');
require('./merge_request_tabs');
(function() {
@@ -24,12 +24,18 @@ require('./merge_request_tabs');
};
})(this));
this.initTabs();
- // Prevent duplicate event bindings
- this.disableTaskList();
this.initMRBtnListeners();
this.initCommitMessageListeners();
if ($("a.btn-close").length) {
- this.initTaskList();
+ this.taskList = new gl.TaskList({
+ dataType: 'merge_request',
+ fieldName: 'description',
+ selector: '.detail-page-description',
+ onSuccess: (result) => {
+ document.querySelector('#task_status').innerText = result.task_status;
+ document.querySelector('#task_status_short').innerText = result.task_status_short;
+ }
+ });
}
}
@@ -50,11 +56,6 @@ require('./merge_request_tabs');
return this.$('.all-commits').removeClass('hide');
};
- MergeRequest.prototype.initTaskList = function() {
- $('.detail-page-description .js-task-list-container').taskList('enable');
- return $(document).on('tasklist:changed', '.detail-page-description .js-task-list-container', this.updateTaskList);
- };
-
MergeRequest.prototype.initMRBtnListeners = function() {
var _this;
_this = this;
@@ -85,30 +86,6 @@ require('./merge_request_tabs');
}
};
- MergeRequest.prototype.disableTaskList = function() {
- $('.detail-page-description .js-task-list-container').taskList('disable');
- return $(document).off('tasklist:changed', '.detail-page-description .js-task-list-container');
- };
-
- MergeRequest.prototype.updateTaskList = function() {
- var patchData;
- patchData = {};
- patchData['merge_request'] = {
- 'description': $('.js-task-list-field', this).val()
- };
- return $.ajax({
- type: 'PATCH',
- url: $('form.js-issuable-update').attr('action'),
- data: patchData,
- success: function(mergeRequest) {
- document.querySelector('#task_status').innerText = mergeRequest.task_status;
- document.querySelector('#task_status_short').innerText = mergeRequest.task_status_short;
- }
- });
- // TODO (rspeicher): Make the merge request description inline-editable like a
- // note so that we can re-use its form here
- };
-
MergeRequest.prototype.initCommitMessageListeners = function() {
$(document).on('click', 'a.js-with-description-link', function(e) {
var textarea = $('textarea.js-commit-message');
@@ -131,4 +108,4 @@ require('./merge_request_tabs');
return MergeRequest;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6
index 4ab33420e59..88f08bbaa34 100644
--- a/app/assets/javascripts/merge_request_widget.js.es6
+++ b/app/assets/javascripts/merge_request_widget.js.es6
@@ -252,7 +252,6 @@ require('./smart_interval');
$('.ci_widget.ci-error').show();
this.setMergeButtonClass('btn-danger');
}
- this.initMiniPipelineGraph();
};
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
diff --git a/app/assets/javascripts/merged_buttons.js b/app/assets/javascripts/merged_buttons.js
index 527cdc9b698..9548a98f499 100644
--- a/app/assets/javascripts/merged_buttons.js
+++ b/app/assets/javascripts/merged_buttons.js
@@ -42,4 +42,4 @@
return MergedButtons;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 051cb9fe5c5..7fbaeec7882 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -172,4 +172,4 @@
return Milestone;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 2f08aa7fe8b..8df1c8e7f94 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -199,4 +199,4 @@
return MilestoneSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6
index 919fcd0a07b..2145e531331 100644
--- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6
+++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6
@@ -28,7 +28,7 @@
* All dropdown events are fired at the .dropdown-menu's parent element.
*/
bindEvents() {
- $(document).on('shown.bs.dropdown', this.container, this.getBuildsList);
+ $(document).off('shown.bs.dropdown', this.container).on('shown.bs.dropdown', this.container, this.getBuildsList);
}
/**
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 2ae5617206e..b98e6121967 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -83,4 +83,4 @@
return NamespaceSelects;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index a7ccd03b60c..43dc9838977 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -421,4 +421,4 @@
y: h
});
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/network/network.js b/app/assets/javascripts/network/network.js
index 37bf6436fd1..8e7027b44e7 100644
--- a/app/assets/javascripts/network/network.js
+++ b/app/assets/javascripts/network/network.js
@@ -17,4 +17,4 @@
return Network;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js
index b4491354472..e5947586583 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -2,9 +2,8 @@
/* global Network */
/* global ShortcutsNetwork */
-// require everything else in this directory
-function requireAll(context) { return context.keys().map(context); }
-requireAll(require.context('.', false, /^\.\/(?!network_bundle).*\.(js|es6)$/));
+require('./branch_graph');
+require('./network');
(function() {
$(function() {
@@ -19,4 +18,4 @@ requireAll(require.context('.', false, /^\.\/(?!network_bundle).*\.(js|es6)$/));
});
return new ShortcutsNetwork(network_graph.branch_graph);
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 7f763c13b50..cb24f212c66 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -100,4 +100,4 @@
return NewBranchForm;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js
index 41eea78a3e6..747f693726e 100644
--- a/app/assets/javascripts/new_commit_form.js
+++ b/app/assets/javascripts/new_commit_form.js
@@ -30,4 +30,4 @@
return NewCommitForm;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 0464b895d6d..03504255bda 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -11,7 +11,7 @@ require('./dropzone_input');
require('./gfm_auto_complete');
require('vendor/jquery.caret'); // required by jquery.atwho
require('vendor/jquery.atwho');
-require('vendor/task_list');
+require('./task_list');
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@@ -51,7 +51,11 @@ require('vendor/task_list');
this.addBinding();
this.setPollingInterval();
this.setupMainTargetNoteForm();
- this.initTaskList();
+ this.taskList = new gl.TaskList({
+ dataType: 'note',
+ fieldName: 'note',
+ selector: '.notes'
+ });
this.collapseLongCommitList();
// We are in the Merge Requests page so we need another edit form for Changes tab
@@ -125,8 +129,6 @@ require('vendor/task_list');
$(document).off("keydown", ".js-note-text");
$(document).off('click', '.js-comment-resolve-button');
$(document).off("click", '.system-note-commit-list-toggler');
- $('.note .js-task-list-container').taskList('disable');
- return $(document).off('tasklist:changed', '.note .js-task-list-container');
};
Notes.prototype.keydownNoteText = function(e) {
@@ -286,7 +288,7 @@ require('vendor/task_list');
// Update datetime format on the recent note
gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false);
this.collapseLongCommitList();
- this.initTaskList();
+ this.taskList.init();
this.refresh();
return this.updateNotesCount(1);
}
@@ -863,15 +865,6 @@ require('vendor/task_list');
}
};
- Notes.prototype.initTaskList = function() {
- this.enableTaskList();
- return $(document).on('tasklist:changed', '.note .js-task-list-container', this.updateTaskList.bind(this));
- };
-
- Notes.prototype.enableTaskList = function() {
- return $('.note .js-task-list-container').taskList('enable');
- };
-
Notes.prototype.putEditFormInPlace = function($el) {
var $editForm = $(this.getEditFormSelector($el));
var $note = $el.closest('.note');
@@ -896,17 +889,6 @@ require('vendor/task_list');
$editForm.find('.referenced-users').hide();
};
- Notes.prototype.updateTaskList = function(e) {
- var $target = $(e.target);
- var $list = $target.closest('.js-task-list-container');
- var $editForm = $(this.getEditFormSelector($target));
- var $note = $list.closest('.note');
-
- this.putEditFormInPlace($list);
- $editForm.find('#note_note').val($note.find('.original-task-list').val());
- $('form', $list).submit();
- };
-
Notes.prototype.updateNotesCount = function(updateCount) {
return this.notesCountBadge.text(parseInt(this.notesCountBadge.text(), 10) + updateCount);
};
@@ -955,4 +937,4 @@ require('vendor/task_list');
return Notes;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index 926dc35fee8..838356133cd 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -28,4 +28,4 @@
return NotificationsDropdown;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js
index c3d7cc0adfb..5005af90d48 100644
--- a/app/assets/javascripts/notifications_form.js
+++ b/app/assets/javascripts/notifications_form.js
@@ -54,4 +54,4 @@
return NotificationsForm;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index 71719917d0c..7c03c8b72d4 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -126,4 +126,4 @@
return Project;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js
index a6d3ba9eb86..aabdfbf65e2 100644
--- a/app/assets/javascripts/project_avatar.js
+++ b/app/assets/javascripts/project_avatar.js
@@ -17,4 +17,4 @@
return ProjectAvatar;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 04fe84683f3..e01668eabef 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -168,4 +168,4 @@
return ProjectFindFile;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js
index 208f25a0e33..47197db39d3 100644
--- a/app/assets/javascripts/project_fork.js
+++ b/app/assets/javascripts/project_fork.js
@@ -10,4 +10,4 @@
return ProjectFork;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index d7943959238..08334bf1ec5 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -10,4 +10,4 @@
return ProjectImport;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_label_subscription.js.es6 b/app/assets/javascripts/project_label_subscription.js.es6
index 8365f7118d5..0a811627600 100644
--- a/app/assets/javascripts/project_label_subscription.js.es6
+++ b/app/assets/javascripts/project_label_subscription.js.es6
@@ -38,13 +38,15 @@
this.$buttons.attr('data-status', newStatus);
this.$buttons.find('> span').text(newAction);
- for (const button of this.$buttons) {
+ this.$buttons.map((button) => {
const $button = $(button);
if ($button.attr('data-original-title')) {
$button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle');
}
- }
+
+ return button;
+ });
});
}
}
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js
index 3aa6f6771ce..e9927c1bf51 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/project_new.js
@@ -101,4 +101,4 @@
return ProjectNew;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 7b5e9953598..f80e765ce30 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -101,4 +101,4 @@
return ProjectSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js
index aad130cf267..3a51c1f26ac 100644
--- a/app/assets/javascripts/project_show.js
+++ b/app/assets/javascripts/project_show.js
@@ -6,6 +6,6 @@
return ProjectShow;
})();
-}).call(this);
+}).call(window);
// I kept class for future
diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js
index 69a11dfaf39..acdf9b7eb5a 100644
--- a/app/assets/javascripts/projects_list.js
+++ b/app/assets/javascripts/projects_list.js
@@ -47,4 +47,4 @@
});
}
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
index bdbad93ad04..48cae8a4fa9 100644
--- a/app/assets/javascripts/render_gfm.js
+++ b/app/assets/javascripts/render_gfm.js
@@ -12,4 +12,4 @@
$(document).on('ready load', function() {
return $('body').renderGFM();
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js
index 6cef449babf..76c61c001ba 100644
--- a/app/assets/javascripts/render_math.js
+++ b/app/assets/javascripts/render_math.js
@@ -51,4 +51,4 @@
});
}
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 76a0f993ea0..903862cac6b 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -21,11 +21,16 @@
};
Sidebar.prototype.addEventListeners = function() {
+ const $document = $(document);
+ const throttledSetSidebarHeight = _.throttle(this.setSidebarHeight, 10);
+
this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
$('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
- $(document).on('click', '.js-sidebar-toggle', function(e, triggered) {
+ $(window).on('resize', () => throttledSetSidebarHeight());
+ $document.on('scroll', () => throttledSetSidebarHeight());
+ $document.on('click', '.js-sidebar-toggle', function(e, triggered) {
var $allGutterToggleIcons, $this, $thisIcon;
e.preventDefault();
$this = $(this);
@@ -191,6 +196,17 @@
}
};
+ Sidebar.prototype.setSidebarHeight = function() {
+ const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
+ const $rightSidebar = $('.js-right-sidebar');
+ const diff = $navHeight - $('body').scrollTop();
+ if (diff > 0) {
+ $rightSidebar.outerHeight($(window).height() - diff);
+ } else {
+ $rightSidebar.outerHeight('100%');
+ }
+ };
+
Sidebar.prototype.isOpen = function() {
return this.sidebar.is('.right-sidebar-expanded');
};
@@ -201,4 +217,4 @@
return Sidebar;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index b1c0dc37b4d..e66418beeab 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -97,4 +97,4 @@
return Search;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/search_autocomplete.js.es6 b/app/assets/javascripts/search_autocomplete.js.es6
index 6250e75d407..6fd5345a0a6 100644
--- a/app/assets/javascripts/search_autocomplete.js.es6
+++ b/app/assets/javascripts/search_autocomplete.js.es6
@@ -169,10 +169,10 @@
url: issuesPath + "/?author_username=" + userName
}, 'separator', {
text: 'Merge requests assigned to me',
- url: mrPath + "/?assignee_id=" + userId
+ url: mrPath + "/?assignee_username=" + userName
}, {
text: "Merge requests I've created",
- url: mrPath + "/?author_id=" + userId
+ url: mrPath + "/?author_username=" + userName
}
];
if (!name) {
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index c6d9b007ad1..81766f4bd55 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -97,4 +97,4 @@
}
};
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js
index 7378b322426..e7baea894f6 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js
@@ -37,4 +37,4 @@ require('./shortcuts');
return ShortcutsDashboardNavigation;
})(Shortcuts);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 36e379d634d..a27ac264a5c 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -35,4 +35,4 @@ require('./shortcuts_navigation');
return ShortcutsFindFile;
})(ShortcutsNavigation);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index b841abb754d..fe58e98cee5 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -89,4 +89,4 @@ require('./shortcuts_navigation');
return ShortcutsIssuable;
})(ShortcutsNavigation);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index cb5f2c53ea6..542cd586df0 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -65,4 +65,4 @@ require('./shortcuts');
return ShortcutsNavigation;
})(Shortcuts);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js
index 651957f5325..4c2bf8bf001 100644
--- a/app/assets/javascripts/shortcuts_network.js
+++ b/app/assets/javascripts/shortcuts_network.js
@@ -25,4 +25,4 @@ require('./shortcuts_navigation');
return ShortcutsNetwork;
})(ShortcutsNavigation);
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6
deleted file mode 100644
index 33e4b7db681..00000000000
--- a/app/assets/javascripts/sidebar.js.es6
+++ /dev/null
@@ -1,111 +0,0 @@
-/* eslint-disable arrow-parens, class-methods-use-this, no-param-reassign */
-/* global Cookies */
-
-(() => {
- const pinnedStateCookie = 'pin_nav';
- const sidebarBreakpoint = 1024;
-
- const pageSelector = '.page-with-sidebar';
- const navbarSelector = '.navbar-gitlab';
- const sidebarWrapperSelector = '.sidebar-wrapper';
- const sidebarContentSelector = '.nav-sidebar';
-
- const pinnedToggleSelector = '.js-nav-pin';
- const sidebarToggleSelector = '.toggle-nav-collapse, .side-nav-toggle';
-
- const pinnedPageClass = 'page-sidebar-pinned';
- const expandedPageClass = 'page-sidebar-expanded';
-
- const pinnedNavbarClass = 'header-sidebar-pinned';
- const expandedNavbarClass = 'header-sidebar-expanded';
-
- class Sidebar {
- constructor() {
- if (!Sidebar.singleton) {
- Sidebar.singleton = this;
- Sidebar.singleton.init();
- }
-
- return Sidebar.singleton;
- }
-
- init() {
- this.isPinned = Cookies.get(pinnedStateCookie) === 'true';
- this.isExpanded = (
- window.innerWidth >= sidebarBreakpoint &&
- $(pageSelector).hasClass(expandedPageClass)
- );
- $(window).on('resize', () => this.setSidebarHeight());
- $(document)
- .on('click', sidebarToggleSelector, () => this.toggleSidebar())
- .on('click', pinnedToggleSelector, () => this.togglePinnedState())
- .on('click', 'html, body, a, button', (e) => this.handleClickEvent(e))
- .on('DOMContentLoaded', () => this.renderState())
- .on('scroll', () => this.setSidebarHeight())
- .on('todo:toggle', (e, count) => this.updateTodoCount(count));
- this.renderState();
- this.setSidebarHeight();
- }
-
- handleClickEvent(e) {
- if (this.isExpanded && (!this.isPinned || window.innerWidth < sidebarBreakpoint)) {
- const $target = $(e.target);
- const targetIsToggle = $target.closest(sidebarToggleSelector).length > 0;
- const targetIsSidebar = $target.closest(sidebarWrapperSelector).length > 0;
- if (!targetIsToggle && (!targetIsSidebar || $target.closest('a'))) {
- this.toggleSidebar();
- }
- }
- }
-
- updateTodoCount(count) {
- $('.js-todos-count').text(gl.text.addDelimiter(count));
- }
-
- toggleSidebar() {
- this.isExpanded = !this.isExpanded;
- this.renderState();
- }
-
- setSidebarHeight() {
- const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
- const diff = $navHeight - $('body').scrollTop();
- if (diff > 0) {
- $('.js-right-sidebar').outerHeight($(window).height() - diff);
- } else {
- $('.js-right-sidebar').outerHeight('100%');
- }
- }
-
- togglePinnedState() {
- this.isPinned = !this.isPinned;
- if (!this.isPinned) {
- this.isExpanded = false;
- }
- Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { expires: 3650 });
- this.renderState();
- }
-
- renderState() {
- $(pageSelector)
- .toggleClass(pinnedPageClass, this.isPinned && this.isExpanded)
- .toggleClass(expandedPageClass, this.isExpanded);
- $(navbarSelector)
- .toggleClass(pinnedNavbarClass, this.isPinned && this.isExpanded)
- .toggleClass(expandedNavbarClass, this.isExpanded);
-
- const $pinnedToggle = $(pinnedToggleSelector);
- const tooltipText = this.isPinned ? 'Unpin navigation' : 'Pin navigation';
- const tooltipState = $pinnedToggle.attr('aria-describedby') && this.isExpanded ? 'show' : 'hide';
- $pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState);
-
- if (this.isExpanded) {
- const sidebarContent = $(sidebarContentSelector);
- setTimeout(() => { sidebarContent.niceScroll().updateScrollBar(); }, 200);
- }
- }
- }
-
- window.gl = window.gl || {};
- gl.Sidebar = Sidebar;
-})();
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 3ee0c73a8d2..294d087554e 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -95,4 +95,4 @@
}
});
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js
index 64f9065be42..89822246bb8 100644
--- a/app/assets/javascripts/snippet/snippet_bundle.js
+++ b/app/assets/javascripts/snippet/snippet_bundle.js
@@ -13,4 +13,4 @@ requireAll(require.context('.', false, /^\.\/(?!snippet_bundle).*\.(js|es6)$/));
$(".snippet-file-content").val(editor.getValue());
});
});
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index 531fd0e9c32..c75b44cc2fd 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -27,4 +27,4 @@
return Star;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js
index 187356f0bf9..8b25f43ffc7 100644
--- a/app/assets/javascripts/subscription_select.js
+++ b/app/assets/javascripts/subscription_select.js
@@ -31,4 +31,4 @@
return SubscriptionSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index 115716bff6a..7c063fae045 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -24,4 +24,4 @@
}
}
};
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js
new file mode 100644
index 00000000000..dfe24d1fb33
--- /dev/null
+++ b/app/assets/javascripts/task_list.js
@@ -0,0 +1,40 @@
+require('vendor/task_list');
+
+class TaskList {
+ constructor(options = {}) {
+ this.selector = options.selector;
+ this.dataType = options.dataType;
+ this.fieldName = options.fieldName;
+ this.onSuccess = options.onSuccess || (() => {});
+ this.init();
+ }
+
+ init() {
+ // Prevent duplicate event bindings
+ this.disable();
+ $(`${this.selector} .js-task-list-container`).taskList('enable');
+ $(document).on('tasklist:changed', `${this.selector} .js-task-list-container`, this.update.bind(this));
+ }
+
+ disable() {
+ $(`${this.selector} .js-task-list-container`).taskList('disable');
+ $(document).off('tasklist:changed', `${this.selector} .js-task-list-container`);
+ }
+
+ update(e) {
+ const $target = $(e.target);
+ const patchData = {};
+ patchData[this.dataType] = {
+ [this.fieldName]: $target.val(),
+ };
+ return $.ajax({
+ type: 'PATCH',
+ url: $target.data('update-url') || $('form.js-issuable-update').attr('action'),
+ data: patchData,
+ success: this.onSuccess,
+ });
+ }
+}
+
+window.gl = window.gl || {};
+window.gl.TaskList = TaskList;
diff --git a/app/assets/javascripts/todos.js.es6 b/app/assets/javascripts/todos.js.es6
index ded683f2ca1..e9513725d9d 100644
--- a/app/assets/javascripts/todos.js.es6
+++ b/app/assets/javascripts/todos.js.es6
@@ -1,28 +1,34 @@
-/* eslint-disable class-methods-use-this, no-new, func-names, prefer-template, no-unneeded-ternary, object-shorthand, space-before-function-paren, comma-dangle, quote-props, consistent-return, no-else-return, no-param-reassign, max-len */
+/* eslint-disable class-methods-use-this, no-new, func-names, no-unneeded-ternary, object-shorthand, quote-props, no-param-reassign, max-len */
/* global UsersSelect */
((global) => {
class Todos {
- constructor({ el } = {}) {
- this.allDoneClicked = this.allDoneClicked.bind(this);
- this.doneClicked = this.doneClicked.bind(this);
- this.el = el || $('.js-todos-options');
- this.perPage = this.el.data('perPage');
- this.clearListeners();
- this.initBtnListeners();
+ constructor() {
this.initFilters();
+ this.bindEvents();
+
+ this.cleanupWrapper = this.cleanup.bind(this);
+ document.addEventListener('beforeunload', this.cleanupWrapper);
}
- clearListeners() {
- $('.done-todo').off('click');
- $('.js-todos-mark-all').off('click');
- return $('.todo').off('click');
+ cleanup() {
+ this.unbindEvents();
+ document.removeEventListener('beforeunload', this.cleanupWrapper);
}
- initBtnListeners() {
- $('.done-todo').on('click', this.doneClicked);
- $('.js-todos-mark-all').on('click', this.allDoneClicked);
- return $('.todo').on('click', this.goToTodoUrl);
+ unbindEvents() {
+ $('.js-done-todo, .js-undo-todo').off('click', this.updateStateClickedWrapper);
+ $('.js-todos-mark-all').off('click', this.allDoneClickedWrapper);
+ $('.todo').off('click', this.goToTodoUrl);
+ }
+
+ bindEvents() {
+ this.updateStateClickedWrapper = this.updateStateClicked.bind(this);
+ this.allDoneClickedWrapper = this.allDoneClicked.bind(this);
+
+ $('.js-done-todo, .js-undo-todo').on('click', this.updateStateClickedWrapper);
+ $('.js-todos-mark-all').on('click', this.allDoneClickedWrapper);
+ $('.todo').on('click', this.goToTodoUrl);
}
initFilters() {
@@ -33,7 +39,7 @@
$('form.filter-form').on('submit', function (event) {
event.preventDefault();
- gl.utils.visitUrl(this.action + '&' + $(this).serialize());
+ gl.utils.visitUrl(`${this.action}&${$(this).serialize()}`);
});
}
@@ -44,105 +50,72 @@
filterable: searchFields ? true : false,
search: { fields: searchFields },
data: $dropdown.data('data'),
- clicked: function() {
+ clicked: function () {
return $dropdown.closest('form.filter-form').submit();
- }
+ },
});
}
- doneClicked(e) {
+ updateStateClicked(e) {
e.preventDefault();
- e.stopImmediatePropagation();
- const $target = $(e.currentTarget);
- $target.disable();
- return $.ajax({
+ const target = e.target;
+ target.setAttribute('disabled', '');
+ target.classList.add('disabled');
+ $.ajax({
type: 'POST',
- url: $target.attr('href'),
+ url: target.getAttribute('href'),
dataType: 'json',
data: {
- '_method': 'delete'
+ '_method': target.getAttribute('data-method'),
},
success: (data) => {
- this.redirectIfNeeded(data.count);
- this.clearDone($target.closest('li'));
- return this.updateBadges(data);
- }
+ this.updateState(target);
+ this.updateBadges(data);
+ },
});
}
allDoneClicked(e) {
e.preventDefault();
- e.stopImmediatePropagation();
const $target = $(e.currentTarget);
$target.disable();
- return $.ajax({
+ $.ajax({
type: 'POST',
url: $target.attr('href'),
dataType: 'json',
data: {
- '_method': 'delete'
+ '_method': 'delete',
},
success: (data) => {
$target.remove();
$('.js-todos-all').html('<div class="nothing-here-block">You\'re all done!</div>');
- return this.updateBadges(data);
- }
+ this.updateBadges(data);
+ },
});
}
- clearDone($row) {
- const $ul = $row.closest('ul');
- $row.remove();
- if (!$ul.find('li').length) {
- return $ul.parents('.panel').remove();
+ updateState(target) {
+ const row = target.closest('li');
+ const restoreBtn = row.querySelector('.js-undo-todo');
+ const doneBtn = row.querySelector('.js-done-todo');
+
+ target.removeAttribute('disabled');
+ target.classList.remove('disabled');
+ target.classList.add('hidden');
+
+ if (target === doneBtn) {
+ row.classList.add('done-reversible');
+ restoreBtn.classList.remove('hidden');
+ } else {
+ row.classList.remove('done-reversible');
+ doneBtn.classList.remove('hidden');
}
}
updateBadges(data) {
$(document).trigger('todo:toggle', data.count);
$('.todos-pending .badge').text(data.count);
- return $('.todos-done .badge').text(data.done_count);
- }
-
- getTotalPages() {
- return this.el.data('totalPages');
- }
-
- getCurrentPage() {
- return this.el.data('currentPage');
- }
-
- getTodosPerPage() {
- return this.el.data('perPage');
- }
-
- redirectIfNeeded(total) {
- const currPages = this.getTotalPages();
- const currPage = this.getCurrentPage();
-
- // Refresh if no remaining Todos
- if (!total) {
- window.location.reload();
- return;
- }
- // Do nothing if no pagination
- if (!currPages) {
- return;
- }
-
- const newPages = Math.ceil(total / this.getTodosPerPage());
- let url = location.href;
-
- if (newPages !== currPages) {
- // Redirect to previous page if there's one available
- if (currPages > 1 && currPage === currPages) {
- const pageParams = {
- page: currPages - 1
- };
- url = gl.utils.mergeUrlParams(pageParams, url);
- }
- return gl.utils.visitUrl(url);
- }
+ $('.todos-done .badge').text(data.done_count);
}
goToTodoUrl(e) {
@@ -159,12 +132,12 @@
if (selected.tagName === 'IMG') {
const avatarUrl = selected.parentElement.getAttribute('href');
- return window.open(avatarUrl, windowTarget);
+ window.open(avatarUrl, windowTarget);
} else {
- return window.open(todoLink, windowTarget);
+ window.open(todoLink, windowTarget);
}
} else {
- return gl.utils.visitUrl(todoLink);
+ gl.utils.visitUrl(todoLink);
}
}
}
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index b1b35fdbd6c..76a821c7a17 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -65,4 +65,4 @@
return TreeView;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js
index 86b459e1866..fd1829efe18 100644
--- a/app/assets/javascripts/u2f/error.js
+++ b/app/assets/javascripts/u2f/error.js
@@ -24,4 +24,4 @@
return U2FError;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 69d1ff3a39e..17631f2908d 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -95,4 +95,4 @@
return U2FRegister;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js
index 34e88220b12..813d363db00 100644
--- a/app/assets/javascripts/u2f/util.js
+++ b/app/assets/javascripts/u2f/util.js
@@ -9,4 +9,4 @@
return U2FUtil;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
index 6e40dfdf3d8..5111b260e1c 100644
--- a/app/assets/javascripts/users/calendar.js
+++ b/app/assets/javascripts/users/calendar.js
@@ -221,4 +221,4 @@
return Calendar;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index d4b24d13299..de33a31b411 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -432,4 +432,4 @@
return UsersSelect;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/javascripts/version_check_image.js.es6 b/app/assets/javascripts/version_check_image.js.es6
index 1fa2b5ac399..d4f716acb72 100644
--- a/app/assets/javascripts/version_check_image.js.es6
+++ b/app/assets/javascripts/version_check_image.js.es6
@@ -1,10 +1,10 @@
-(() => {
- class VersionCheckImage {
- static bindErrorEvent(imageElement) {
- imageElement.off('error').on('error', () => imageElement.hide());
- }
+class VersionCheckImage {
+ static bindErrorEvent(imageElement) {
+ imageElement.off('error').on('error', () => imageElement.hide());
}
+}
- window.gl = window.gl || {};
- gl.VersionCheckImage = VersionCheckImage;
-})();
+window.gl = window.gl || {};
+gl.VersionCheckImage = VersionCheckImage;
+
+module.exports = VersionCheckImage;
diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
index 83e045c6d3d..9d66d28cc62 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
@@ -29,7 +29,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
},
props: ['scope', 'store', 'svgs'],
created() {
- const pagenum = gl.utils.getParameterByName('p');
+ const pagenum = gl.utils.getParameterByName('page');
const scope = gl.utils.getParameterByName('scope');
if (pagenum) this.pagenum = pagenum;
if (scope) this.apiScope = scope;
@@ -44,7 +44,6 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
},
methods: {
-
/**
* Changes the URL according to the pagination component.
*
@@ -57,7 +56,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
*/
change(pagenum, apiScope) {
if (!apiScope) apiScope = 'all';
- gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
+ gl.utils.visitUrl(`?scope=${apiScope}&page=${pagenum}`);
},
},
template: `
diff --git a/app/assets/javascripts/wikis.js.es6 b/app/assets/javascripts/wikis.js.es6
index ef99b2e92f0..75fd1394a03 100644
--- a/app/assets/javascripts/wikis.js.es6
+++ b/app/assets/javascripts/wikis.js.es6
@@ -1,14 +1,10 @@
/* eslint-disable no-param-reassign */
/* global Breakpoints */
-require('vendor/latinise');
require('./breakpoints');
require('vendor/jquery.nicescroll');
((global) => {
- const dasherize = str => str.replace(/[_\s]+/g, '-');
- const slugify = str => dasherize(str.trim().toLowerCase().latinise());
-
class Wikis {
constructor() {
this.bp = Breakpoints.get();
@@ -34,7 +30,7 @@ require('vendor/jquery.nicescroll');
if (!this.newWikiForm) return;
const slugInput = this.newWikiForm.querySelector('#new_wiki_path');
- const slug = slugify(slugInput.value);
+ const slug = gl.text.slugify(slugInput.value);
if (slug.length > 0) {
const wikisPath = slugInput.getAttribute('data-wikis-path');
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index d9261cda1b1..ce626cf7b46 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -94,4 +94,4 @@ require('mousetrap/plugins/pause/mousetrap-pause');
return ZenMode;
})();
-}).call(this);
+}).call(window);
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 08f203a1bf6..39cf3b5f8ae 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -19,7 +19,6 @@
@import "framework/flash.scss";
@import "framework/forms.scss";
@import "framework/gfm.scss";
-@import "framework/gitlab-theme.scss";
@import "framework/header.scss";
@import "framework/highlight.scss";
@import "framework/issue_box.scss";
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 0ca5a9343f7..90935b9616b 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -116,7 +116,7 @@
}
.btn,
-.side-nav-toggle {
+.global-dropdown-toggle {
@include transition(background-color, border-color, color, box-shadow);
}
@@ -140,7 +140,6 @@ a {
@include transition(background-color, box-shadow);
}
-.nav-sidebar a,
.dropdown-menu a,
.dropdown-menu button,
.dropdown-menu-nav a {
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
deleted file mode 100644
index d6566dc4ec9..00000000000
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * Styles the GitLab application with a specific color theme
- *
- * $color-light -
- * $color -
- * $color-darker -
- * $color-dark -
- */
-@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
- .page-with-sidebar {
- .toggle-nav-collapse,
- .pin-nav-btn {
- color: $color-light;
-
- &:hover {
- color: $white-light;
- }
- }
-
- .sidebar-wrapper {
- background: $color-darker;
- }
-
- .sidebar-action-buttons {
- color: $color-light;
- background-color: lighten($color-darker, 5%);
- }
-
- .nav-sidebar {
- li {
- a {
- color: $color-light;
-
- &:hover,
- &:focus,
- &:active {
- background: $color-dark;
- }
-
- i {
- color: $color-light;
- }
-
- path,
- polygon {
- fill: $color-light;
- }
-
- .count {
- color: $color-light;
- background: $color-dark;
- }
-
- svg {
- position: relative;
- top: 3px;
- }
- }
-
- &.separate-item {
- border-top: 1px solid $color;
- }
-
- &.active a {
- color: $white-light;
- background: $color-dark;
-
- &.no-highlight {
- border: none;
- }
-
- i {
- color: $white-light;
- }
-
- path,
- polygon {
- fill: $white-light;
- }
- }
- }
-
- .about-gitlab {
- color: $color-light;
- }
- }
- }
-}
-
-$theme-charcoal-light: #b9bbbe;
-$theme-charcoal: #485157;
-$theme-charcoal-dark: #3d454d;
-$theme-charcoal-darker: #383f45;
-
-$theme-blue-light: #becde9;
-$theme-blue: #2980b9;
-$theme-blue-dark: #1970a9;
-$theme-blue-darker: #096099;
-
-$theme-graphite-light: #ccc;
-$theme-graphite: #777;
-$theme-graphite-dark: #666;
-$theme-graphite-darker: #555;
-
-$theme-black-light: #979797;
-$theme-black: #373737;
-$theme-black-dark: #272727;
-$theme-black-darker: #222;
-
-$theme-green-light: #adc;
-$theme-green: #019875;
-$theme-green-dark: #018865;
-$theme-green-darker: #017855;
-
-$theme-violet-light: #98c;
-$theme-violet: #548;
-$theme-violet-dark: #436;
-$theme-violet-darker: #325;
-
-body {
- &.ui_blue {
- @include gitlab-theme($theme-blue-light, $theme-blue, $theme-blue-dark, $theme-blue-darker);
- }
-
- &.ui_charcoal {
- @include gitlab-theme($theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark, $theme-charcoal-darker);
- }
-
- &.ui_graphite {
- @include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker);
- }
-
- &.ui_black {
- @include gitlab-theme($theme-black-light, $theme-black, $theme-black-dark, $theme-black-darker);
- }
-
- &.ui_green {
- @include gitlab-theme($theme-green-light, $theme-green, $theme-green-dark, $theme-green-darker);
- }
-
- &.ui_violet {
- @include gitlab-theme($theme-violet-light, $theme-violet, $theme-violet-dark, $theme-violet-darker);
- }
-}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 34e010e0e8a..3945a789c82 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -100,23 +100,40 @@ header {
}
}
}
+ }
- .side-nav-toggle {
- position: absolute;
- left: -10px;
- margin: 7px 0;
- font-size: 18px;
- padding: 6px 10px;
- border: none;
- background-color: $gray-light;
+ .global-dropdown {
+ position: absolute;
+ left: -10px;
- &:hover {
- background-color: $white-normal;
- color: $gl-header-nav-hover-color;
+ .badge {
+ font-size: 11px;
+ }
+
+ li {
+ &.active a {
+ font-weight: bold;
}
}
}
+ .global-dropdown-toggle {
+ margin: 7px 0;
+ font-size: 18px;
+ padding: 6px 10px;
+ border: none;
+ background-color: $gray-light;
+
+ &:hover {
+ background-color: $white-normal;
+ }
+
+ &:focus {
+ outline: none;
+ background-color: $white-normal;
+ }
+ }
+
.header-content {
position: relative;
height: $header-height;
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 2bfdb9f9601..55ed4b7b06c 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -96,16 +96,6 @@ ul.unstyled-list > li {
border-bottom: none;
}
-ul.task-list {
- li.task-list-item {
- list-style-type: none;
- }
-
- ul:not(.task-list) {
- padding-left: 1.3em;
- }
-}
-
// Generic content list
ul.content-list {
@include basic-list;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 1acd06122a3..df78bbdea51 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -76,6 +76,13 @@
#{$property}: $value;
}
+/* http://phrappe.com/css/conditional-css-for-webkit-based-browsers/ */
+@mixin on-webkit-only {
+ @media screen and (-webkit-min-device-pixel-ratio:0) {
+ @content;
+ }
+}
+
@mixin keyframes($animation-name) {
@-webkit-keyframes #{$animation-name} {
@content;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 20bcb1eeb23..040a7ce0c16 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -1,36 +1,3 @@
-.page-with-sidebar {
- padding-bottom: 25px;
- transition: padding $sidebar-transition-duration;
-
- &.page-sidebar-pinned {
- .sidebar-wrapper {
- box-shadow: none;
- }
- }
-
- .sidebar-wrapper {
- position: fixed;
- top: 0;
- bottom: 0;
- left: 0;
- height: 100%;
- width: 0;
- overflow: hidden;
- transition: width $sidebar-transition-duration;
- box-shadow: 2px 0 16px 0 $black-transparent;
- }
-}
-
-.sidebar-wrapper {
- z-index: 1000;
- background: $gray-light;
-
- .nicescroll-rails-hr {
- // TODO: Figure out why nicescroll doesn't hide horizontal bar
- display: none!important;
- }
-}
-
.content-wrapper {
width: 100%;
transition: padding $sidebar-transition-duration;
@@ -47,105 +14,6 @@
}
}
-.nav-sidebar {
- position: absolute;
- top: 50px;
- bottom: 0;
- width: $sidebar_width;
- overflow-y: auto;
- overflow-x: hidden;
-
- &.navbar-collapse {
- padding: 0 !important;
- }
-
- li {
- &.separate-item {
- padding-top: 10px;
- margin-top: 10px;
- }
-
- .icon-container {
- width: 34px;
- display: inline-block;
- text-align: center;
- }
-
- a {
- padding: 7px $gl-sidebar-padding;
- font-size: $gl-font-size;
- line-height: 24px;
- display: block;
- text-decoration: none;
- font-weight: normal;
-
- &:hover,
- &:active,
- &:focus {
- text-decoration: none;
- }
-
- i {
- font-size: 16px;
- }
-
- i,
- svg {
- margin-right: 13px;
- }
- }
- }
-
- .count {
- float: right;
- padding: 0 8px;
- border-radius: 6px;
- }
-
- .about-gitlab {
- padding: 7px $gl-sidebar-padding;
- font-size: $gl-font-size;
- line-height: 24px;
- display: block;
- text-decoration: none;
- font-weight: normal;
- position: absolute;
- bottom: 10px;
- }
-}
-
-.sidebar-action-buttons {
- width: $sidebar_width;
- position: absolute;
- top: 0;
- left: 0;
- min-height: 50px;
- padding: 5px 0;
- font-size: 18px;
- line-height: 30px;
-
- .toggle-nav-collapse {
- left: 0;
- }
-
- .pin-nav-btn {
- right: 0;
- display: none;
-
- @media (min-width: $sidebar-breakpoint) {
- display: block;
- }
-
- .fa {
- transition: transform .15s;
-
- .page-sidebar-pinned & {
- transform: rotate(90deg);
- }
- }
- }
-}
-
.nav-header-btn {
padding: 10px $gl-sidebar-padding;
color: inherit;
@@ -161,46 +29,9 @@
}
}
-.page-sidebar-expanded {
- .sidebar-wrapper {
- width: $sidebar_width;
- }
-}
-
-.page-sidebar-pinned {
- .content-wrapper,
- .layout-nav {
- @media (min-width: $sidebar-breakpoint) {
- padding-left: $sidebar_width;
- }
- }
-
- .merge-request-tabs-holder.affix {
- @media (min-width: $sidebar-breakpoint) {
- left: $sidebar_width;
- }
- }
-
- &.right-sidebar-expanded {
- .line-resolve-all-container {
- @media (min-width: $sidebar-breakpoint) {
- display: none;
- }
- }
- }
-}
-
-header.header-sidebar-pinned {
- @media (min-width: $sidebar-breakpoint) {
- padding-left: ($sidebar_width + $gl-padding);
-
- .side-nav-toggle {
- display: none;
- }
-
- .header-content {
- padding-left: 0;
- }
+@media (min-width: $screen-sm-min) {
+ .content-wrapper {
+ padding-right: $gutter_collapsed_width;
}
}
@@ -208,12 +39,8 @@ header.header-sidebar-pinned {
padding-right: 0;
@media (min-width: $screen-sm-min) {
- .content-wrapper {
- padding-right: $sidebar_collapsed_width;
- }
-
.merge-request-tabs-holder.affix {
- right: $sidebar_collapsed_width;
+ right: $gutter_collapsed_width;
}
}
@@ -229,12 +56,6 @@ header.header-sidebar-pinned {
.right-sidebar-expanded {
padding-right: 0;
- @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
- &:not(.build-sidebar):not(.wiki-sidebar) {
- padding-right: $sidebar_collapsed_width;
- }
- }
-
@media (min-width: $screen-md-min) {
.content-wrapper {
padding-right: $gutter_width;
@@ -245,12 +66,12 @@ header.header-sidebar-pinned {
}
&.with-overlay .merge-request-tabs-holder.affix {
- right: $sidebar_collapsed_width;
+ right: $gutter_collapsed_width;
}
}
&.with-overlay {
- padding-right: $sidebar_collapsed_width;
+ padding-right: $gutter_collapsed_width;
}
}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 54958973f15..db5e2c51fe7 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -134,7 +134,7 @@
ul,
ol {
padding: 0;
- margin: 3px 0 3px 28px !important;
+ margin: 3px 0 !important;
}
ul:dir(rtl),
@@ -144,6 +144,29 @@
li {
line-height: 1.6em;
+ margin-left: 25px;
+ padding-left: 3px;
+
+ /* Normalize the bullet position on webkit. */
+ @include on-webkit-only {
+ margin-left: 28px;
+ padding-left: 0;
+ }
+ }
+
+ ul.task-list {
+ li.task-list-item {
+ list-style-type: none;
+ position: relative;
+ padding-left: 28px;
+ margin-left: 0 !important;
+
+ input.task-list-item-checkbox {
+ position: absolute;
+ left: 8px;
+ top: 5px;
+ }
+ }
}
a[href*="/uploads/"],
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 7809d4866f1..ba0af072716 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -1,8 +1,6 @@
/*
* Layout
*/
-$sidebar_collapsed_width: 62px;
-$sidebar_width: 220px;
$gutter_collapsed_width: 62px;
$gutter_width: 290px;
$gutter_inner_width: 250px;
@@ -541,4 +539,4 @@ Pipeline Graph
*/
$stage-hover-bg: #eaf3fc;
$stage-hover-border: #d1e7fc;
-$action-icon-color: #d6d6d6; \ No newline at end of file
+$action-icon-color: #d6d6d6;
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 181dcb7721f..f789ae1ccd3 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -35,7 +35,6 @@
display: table-cell;
}
- .environments-name,
.environments-commit,
.environments-actions {
width: 20%;
@@ -45,6 +44,7 @@
width: 10%;
}
+ .environments-name,
.environments-deploy,
.environments-build {
width: 15%;
@@ -62,6 +62,22 @@
}
}
+ .btn-group {
+
+ > a {
+ color: $gl-text-color-secondary;
+ }
+
+ svg path {
+ fill: $gl-text-color-secondary;
+ }
+
+ .dropdown {
+ outline: none;
+ }
+ }
+
+
.commit-title {
margin: 0;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index a53cc27fac9..4426169ef5a 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -253,11 +253,11 @@
display: block;
}
- width: $sidebar_collapsed_width;
+ width: $gutter_collapsed_width;
padding-top: 0;
.block {
- width: $sidebar_collapsed_width - 2px;
+ width: $gutter_collapsed_width - 2px;
margin-left: -19px;
padding: 15px 0 0;
border-bottom: none;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 80b0c9493d8..b595480561b 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -10,6 +10,11 @@
.issue-labels {
display: inline-block;
}
+
+ .icon-merge-request-unmerged {
+ height: 13px;
+ margin-bottom: 3px;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 3da1150f89b..27c47d36818 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -30,6 +30,26 @@
word-wrap: break-word;
}
}
+
+ .panel-heading {
+ line-height: $line-height-base;
+ padding: 14px 16px;
+ display: -webkit-flex;
+ display: flex;
+
+ .title {
+ -webkit-flex: 1;
+ -webkit-flex-grow: 1;
+ flex: 1;
+ flex-grow: 2;
+ }
+
+ .counter {
+ -webkit-flex: 1;
+ flex: 0;
+ padding-left: 16px;
+ }
+ }
}
.milestone-summary {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 00eb5b30fd5..3fe1eef307e 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -222,6 +222,11 @@
}
}
+ .dropdown-menu {
+ max-height: 250px;
+ overflow-y: auto;
+ }
+
.dropdown-toggle,
.dropdown-menu {
color: $gl-text-color-secondary;
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index 100ace41f2a..305feaacaa1 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -1,42 +1,3 @@
-.application-theme {
- label {
- margin-right: 20px;
- text-align: center;
-
- .preview {
- border-radius: 4px;
-
- height: 80px;
- margin-bottom: 10px;
- width: 160px;
-
- &.ui_blue {
- background: $theme-blue;
- }
-
- &.ui_charcoal {
- background: $theme-charcoal;
- }
-
- &.ui_graphite {
- background: $theme-graphite;
- }
-
- &.ui_black {
- background: $theme-black;
- }
-
- &.ui_green {
- background: $theme-green;
- }
-
- &.ui_violet {
- background: $theme-violet;
- }
- }
- }
-}
-
.syntax-theme {
label {
margin-right: 20px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 6b05e5bb4aa..67110813abb 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -268,6 +268,13 @@
}
}
+.project-repo-buttons {
+ .project-action-button .dropdown-menu {
+ max-height: 250px;
+ overflow-y: auto;
+ }
+}
+
.split-one {
display: inline-table;
margin-right: 12px;
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 0d5604aae69..af9ddb9ff80 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -6,6 +6,8 @@
.navbar-nav {
li {
.badge.todos-pending-count {
+ position: inherit;
+ top: -6px;
margin-top: -5px;
font-weight: normal;
background: $todo-alert-blue;
@@ -43,6 +45,12 @@
}
}
+ .todo-avatar,
+ .todo-actions {
+ -webkit-flex: 0 0 auto;
+ flex: 0 0 auto;
+ }
+
.todo-actions {
display: -webkit-flex;
display: flex;
@@ -55,15 +63,49 @@
}
.todo-item {
- -webkit-flex: auto;
- flex: auto;
+ -webkit-flex: 0 1 100%;
+ flex: 0 1 100%;
+ min-width: 0;
+ }
+}
+
+.todos-list > .todo.todo-pending.done-reversible {
+ background-color: $gray-light;
+
+ &:hover {
+ border-color: $border-color;
+ }
+
+ .title {
+ font-weight: normal;
}
}
.todo-item {
.todo-title {
- @include str-truncated(calc(100% - 174px));
- overflow: visible;
+ display: flex;
+
+ & > .title-item {
+ -webkit-flex: 0 0 auto;
+ flex: 0 0 auto;
+ margin: 0 2px;
+
+ &:first-child {
+ margin-left: 0;
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ .todo-label {
+ -webkit-flex: 0 1 auto;
+ flex: 0 1 auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
}
.status-box {
@@ -142,10 +184,12 @@
.todo-item {
.todo-title {
- white-space: normal;
- overflow: visible;
- max-width: 100%;
+ flex-flow: row wrap;
margin-bottom: 10px;
+
+ .todo-label {
+ white-space: normal;
+ }
}
.todo-body {
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 948921efc0b..e4487dbcb87 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -149,7 +149,7 @@
}
.commit-actions {
- width: 200px;
+ width: 260px;
}
}
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index 0ff3c3f5472..6cc1cc8e263 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -31,7 +31,6 @@ nav.navbar-collapse.collapse,
.blob-commit-info,
.file-title,
.file-holder,
-.sidebar-wrapper,
.nav,
.btn,
ul.notes-form,
diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb
index 338496013a0..c09095b9849 100644
--- a/app/controllers/admin/background_jobs_controller.rb
+++ b/app/controllers/admin/background_jobs_controller.rb
@@ -2,5 +2,6 @@ class Admin::BackgroundJobsController < Admin::ApplicationController
def show
ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Gitlab.config.gitlab.user} -o pid,pcpu,pmem,stat,start,command))
@sidekiq_processes = ps_output.split("\n").grep(/sidekiq/)
+ @concurrency = Sidekiq.options[:concurrency]
end
end
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 7345c91f67d..348641e5ecb 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -13,7 +13,7 @@ class Admin::RunnersController < Admin::ApplicationController
end
def update
- if @runner.update_attributes(runner_params)
+ if Ci::UpdateRunnerService.new(@runner).update(runner_params)
respond_to do |format|
format.js
format.html { redirect_to admin_runner_path(@runner) }
@@ -31,7 +31,7 @@ class Admin::RunnersController < Admin::ApplicationController
end
def resume
- if @runner.update_attributes(active: true)
+ if Ci::UpdateRunnerService.new(@runner).update(active: true)
redirect_to admin_runners_path, notice: 'Runner was successfully updated.'
else
redirect_to admin_runners_path, alert: 'Runner was not updated.'
@@ -39,7 +39,7 @@ class Admin::RunnersController < Admin::ApplicationController
end
def pause
- if @runner.update_attributes(active: false)
+ if Ci::UpdateRunnerService.new(@runner).update(active: false)
redirect_to admin_runners_path, notice: 'Runner was successfully updated.'
else
redirect_to admin_runners_path, alert: 'Runner was not updated.'
diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb
index ca04a17caa1..1330399a836 100644
--- a/app/controllers/admin/system_info_controller.rb
+++ b/app/controllers/admin/system_info_controller.rb
@@ -21,6 +21,7 @@ class Admin::SystemInfoController < Admin::ApplicationController
'mqueue',
'proc',
'pstore',
+ 'rpc_pipefs',
'securityfs',
'sysfs',
'tmpfs',
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 1cd50852e89..7ffde71c3b1 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -194,7 +194,6 @@ class Admin::UsersController < Admin::ApplicationController
:provider,
:remember_me,
:skype,
- :theme_id,
:twitter,
:username,
:website_url
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index bf6be3d516b..5e7af3bff0d 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -74,7 +74,7 @@ class ApplicationController < ActionController::Base
def authenticate_user!(*args)
if redirect_to_home_page_url?
- redirect_to current_application_settings.home_page_url and return
+ return redirect_to current_application_settings.home_page_url
end
super(*args)
@@ -131,7 +131,7 @@ class ApplicationController < ActionController::Base
headers['X-UA-Compatible'] = 'IE=edge'
headers['X-Content-Type-Options'] = 'nosniff'
# Enabling HSTS for non-standard ports would send clients to the wrong port
- if Gitlab.config.gitlab.https and Gitlab.config.gitlab.port == 443
+ if Gitlab.config.gitlab.https && Gitlab.config.gitlab.port == 443
headers['Strict-Transport-Security'] = 'max-age=31536000'
end
end
@@ -152,7 +152,7 @@ class ApplicationController < ActionController::Base
def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
- redirect_to new_profile_password_path and return
+ return redirect_to new_profile_password_path
end
end
@@ -218,7 +218,7 @@ class ApplicationController < ActionController::Base
def require_email
if current_user && current_user.temp_oauth_email? && session[:impersonator_id].nil?
- redirect_to profile_path, notice: 'Please complete your profile with email address' and return
+ return redirect_to profile_path, notice: 'Please complete your profile with email address'
end
end
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index 6286d67d30c..88d180fcc2e 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -104,23 +104,15 @@ module CreatesCommit
if can?(current_user, :push_code, @project)
# Edit file in this project
@mr_source_project = @project
-
- if @project.forked?
- # Merge request from this project to fork origin
- @mr_target_project = @project.forked_from_project
- @mr_target_branch = @mr_target_project.repository.root_ref
- else
- # Merge request to this project
- @mr_target_project = @project
- @mr_target_branch = @ref || @target_branch
- end
else
# Merge request from fork to this project
@mr_source_project = current_user.fork_of(@project)
- @mr_target_project = @project
- @mr_target_branch = @ref || @target_branch
end
+ # Merge request to this project
+ @mr_target_project = @project
+ @mr_target_branch = @ref || @target_branch
+
@mr_source_branch = guess_mr_source_branch
end
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index a6e158ebae6..85ae4985e58 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -9,24 +9,32 @@ module IssuableCollections
private
- def issuable_meta_data(issuable_collection)
+ def issuable_meta_data(issuable_collection, collection_type)
# map has to be used here since using pluck or select will
# throw an error when ordering issuables by priority which inserts
# a new order into the collection.
# We cannot use reorder to not mess up the paginated collection.
- issuable_ids = issuable_collection.map(&:id)
- issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type)
+ issuable_ids = issuable_collection.map(&:id)
+ issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type)
issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type)
+ issuable_merge_requests_count =
+ if collection_type == 'Issue'
+ MergeRequestsClosingIssues.count_for_collection(issuable_ids)
+ else
+ []
+ end
issuable_ids.each_with_object({}) do |id, issuable_meta|
downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? }
- upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? }
- notes = issuable_note_count.find { |notes| notes.noteable_id == id }
+ upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? }
+ notes = issuable_note_count.find { |notes| notes.noteable_id == id }
+ merge_requests = issuable_merge_requests_count.find { |mr| mr.first == id }
issuable_meta[id] = Issuable::IssuableMeta.new(
upvotes.try(:count).to_i,
downvotes.try(:count).to_i,
- notes.try(:count).to_i
+ notes.try(:count).to_i,
+ merge_requests.try(:last).to_i
)
end
end
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index fb5edb34370..b17c138d5c7 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -10,7 +10,7 @@ module IssuesAction
.page(params[:page])
@collection_type = "Issue"
- @issuable_meta_data = issuable_meta_data(@issues)
+ @issuable_meta_data = issuable_meta_data(@issues, @collection_type)
respond_to do |format|
format.html
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
index 6229759dcf1..d3c8e4888bc 100644
--- a/app/controllers/concerns/merge_requests_action.rb
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -9,7 +9,7 @@ module MergeRequestsAction
.page(params[:page])
@collection_type = "MergeRequest"
- @issuable_meta_data = issuable_meta_data(@merge_requests)
+ @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
end
private
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
new file mode 100644
index 00000000000..ca6dffe1cc5
--- /dev/null
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -0,0 +1,21 @@
+module SnippetsActions
+ extend ActiveSupport::Concern
+
+ def edit
+ end
+
+ def raw
+ send_data(
+ convert_line_endings(@snippet.content),
+ type: 'text/plain; charset=utf-8',
+ disposition: 'inline',
+ filename: @snippet.sanitized_file_name
+ )
+ end
+
+ private
+
+ def convert_line_endings(content)
+ params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n")
+ end
+end
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index a6891149bfa..da225d8f1c7 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -17,13 +17,31 @@ module SpammableActions
private
- def recaptcha_params
- return {} unless params[:recaptcha_verification] && Gitlab::Recaptcha.load_configurations! && verify_recaptcha
+ def recaptcha_check_with_fallback(&fallback)
+ if spammable.valid?
+ redirect_to spammable
+ elsif render_recaptcha?
+ if params[:recaptcha_verification]
+ flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
+ end
+
+ render :verify
+ else
+ fallback.call
+ end
+ end
+
+ def spammable_params
+ default_params = { request: request }
+
+ recaptcha_check = params[:recaptcha_verification] &&
+ Gitlab::Recaptcha.load_configurations! &&
+ verify_recaptcha
+
+ return default_params unless recaptcha_check
- {
- recaptcha_verified: true,
- spam_log_id: params[:spam_log_id]
- }
+ { recaptcha_verified: true,
+ spam_log_id: params[:spam_log_id] }.merge(default_params)
end
def spammable
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index e3933e3d7b1..5848ca62777 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -1,4 +1,6 @@
class Dashboard::TodosController < Dashboard::ApplicationController
+ include ActionView::Helpers::NumberHelper
+
before_action :find_todos, only: [:index, :destroy_all]
def index
@@ -29,6 +31,17 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
end
+ def restore
+ TodoService.new.mark_todos_as_pending_by_ids([params[:id]], current_user)
+
+ render json: todos_counts
+ end
+
+ # Used in TodosHelper also
+ def self.todos_count_format(count)
+ count >= 100 ? '99+' : count
+ end
+
private
def find_todos
@@ -37,8 +50,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def todos_counts
{
- count: current_user.todos_pending_count,
- done_count: current_user.todos_done_count
+ count: number_with_delimiter(current_user.todos_pending_count),
+ done_count: number_with_delimiter(current_user.todos_done_count)
}
end
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 99b10b2f9b3..5df6bd34185 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -29,7 +29,7 @@ class Import::FogbugzController < Import::BaseController
unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? }
flash.now[:alert] = 'All users must have a name.'
- render 'new_user_map' and return
+ return render 'new_user_map'
end
session[:fogbugz_user_map] = user_map
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 8d0de158f98..7d7f13ce5d5 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -44,13 +44,13 @@ class Import::GoogleCodeController < Import::BaseController
rescue
flash.now[:alert] = "The entered user map is not a valid JSON user map."
- render "new_user_map" and return
+ return render "new_user_map"
end
unless user_map.is_a?(Hash) && user_map.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
flash.now[:alert] = "The entered user map is not a valid JSON user map."
- render "new_user_map" and return
+ return render "new_user_map"
end
# This is the default, so let's not save it into the database.
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index 58964a0e65d..7625187c7be 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -42,9 +42,7 @@ class InvitesController < ApplicationController
@token = params[:id]
@member = Member.find_by_invite_token(@token)
- unless @member
- render_404 and return
- end
+ return render_404 unless @member
@member
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index f54c79c2e37..58d50ad647b 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -78,6 +78,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_omniauth
end
+ def authentiq
+ if params['sid']
+ handle_service_ticket oauth['provider'], params['sid']
+ end
+ handle_omniauth
+ end
+
private
def handle_omniauth
@@ -115,7 +122,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
else
error_message = @user.errors.full_messages.to_sentence
- redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
+ return redirect_to omniauth_error_path(oauth['provider'], error: error_message)
end
end
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 830e0b9591b..c8663a3c38e 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -45,13 +45,13 @@ class Profiles::KeysController < Profiles::ApplicationController
if user.present?
render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
else
- render_404 and return
+ return render_404
end
rescue => e
render text: e.message
end
else
- render_404 and return
+ return render_404
end
end
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index a9a06ecc808..0d891ef4004 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -34,7 +34,6 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:layout,
:dashboard,
:project_view,
- :theme_id
)
end
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index db33b60b229..e2f81b09adc 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -83,7 +83,6 @@ class Projects::ApplicationController < ApplicationController
end
def apply_diff_view_cookie!
- @show_changes_tab = params[:view].present?
cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present?
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index a1db856dcfb..39ba815cfca 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -95,7 +95,7 @@ class Projects::BlobController < Projects::ApplicationController
else
if tree = @repository.tree(@commit.id, @path)
if tree.entries.any?
- redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path)) and return
+ return redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path))
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 744a4af1c51..ca5e81100da 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -26,7 +26,7 @@ class Projects::IssuesController < Projects::ApplicationController
@collection_type = "Issue"
@issues = issues_collection
@issues = @issues.page(params[:page])
- @issuable_meta_data = issuable_meta_data(@issues)
+ @issuable_meta_data = issuable_meta_data(@issues, @collection_type)
if @issues.out_of_range? && @issues.total_pages != 0
return redirect_to url_for(params.merge(page: @issues.total_pages))
@@ -94,15 +94,15 @@ class Projects::IssuesController < Projects::ApplicationController
end
def create
- extra_params = { request: request,
- merge_request_for_resolving_discussions: merge_request_for_resolving_discussions }
- extra_params.merge!(recaptcha_params)
+ create_params = issue_params
+ .merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions)
+ .merge(spammable_params)
- @issue = Issues::CreateService.new(project, current_user, issue_params.merge(extra_params)).execute
+ @issue = Issues::CreateService.new(project, current_user, create_params).execute
respond_to do |format|
format.html do
- html_response_create
+ recaptcha_check_with_fallback { render :new }
end
format.js do
@link = @issue.attachment.url.to_js
@@ -111,7 +111,9 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update
- @issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
+ update_params = issue_params.merge(spammable_params)
+
+ @issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
if params[:move_to_project_id].to_i > 0
new_project = Project.find(params[:move_to_project_id])
@@ -123,11 +125,7 @@ class Projects::IssuesController < Projects::ApplicationController
respond_to do |format|
format.html do
- if @issue.valid?
- redirect_to issue_path(@issue)
- else
- render :edit
- end
+ recaptcha_check_with_fallback { render :edit }
end
format.json do
@@ -179,20 +177,6 @@ class Projects::IssuesController < Projects::ApplicationController
protected
- def html_response_create
- if @issue.valid?
- redirect_to issue_path(@issue)
- elsif render_recaptcha?
- if params[:recaptcha_verification]
- flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
- end
-
- render :verify
- else
- render :new
- end
- end
-
def issue
# The Sortable default scope causes performance issues when used with find_by
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take || redirect_old
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 63b5bcbb586..365c49a20d4 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -39,7 +39,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest"
@merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page])
- @issuable_meta_data = issuable_meta_data(@merge_requests)
+ @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
return redirect_to url_for(params.merge(page: @merge_requests.total_pages))
@@ -50,6 +50,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@labels = LabelsFinder.new(current_user, labels_params).execute
end
+ @users = []
+ if params[:assignee_id].present?
+ assignee = User.find_by_id(params[:assignee_id])
+ @users.push(assignee) if assignee
+ end
+
+ if params[:author_id].present?
+ author = User.find_by_id(params[:author_id])
+ @users.push(author) if author
+ end
+
respond_to do |format|
format.html
format.json do
@@ -245,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html do
define_new_vars
+ @show_changes_tab = true
render "new"
end
format.json do
@@ -616,6 +628,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@labels = LabelsFinder.new(current_user, project_id: @project.id).execute
+ @show_changes_tab = params[:show_changes].present?
+
define_pipelines_vars
end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 74c54037ba9..8b50ea207a5 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -12,7 +12,7 @@ class Projects::RunnersController < Projects::ApplicationController
end
def update
- if @runner.update_attributes(runner_params)
+ if Ci::UpdateRunnerService.new(@runner).update(runner_params)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else
render 'edit'
@@ -28,7 +28,7 @@ class Projects::RunnersController < Projects::ApplicationController
end
def resume
- if @runner.update_attributes(active: true)
+ if Ci::UpdateRunnerService.new(@runner).update(active: true)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else
redirect_to runner_path(@runner), alert: 'Runner was not updated.'
@@ -36,7 +36,7 @@ class Projects::RunnersController < Projects::ApplicationController
end
def pause
- if @runner.update_attributes(active: false)
+ if Ci::UpdateRunnerService.new(@runner).update(active: false)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else
redirect_to runner_path(@runner), alert: 'Runner was not updated.'
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 5d193f26a8e..ea1a97b7cf0 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -1,6 +1,7 @@
class Projects::SnippetsController < Projects::ApplicationController
include ToggleAwardEmoji
include SpammableActions
+ include SnippetsActions
before_action :module_enabled
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
@@ -37,27 +38,19 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def create
- create_params = snippet_params.merge(request: request)
- @snippet = CreateSnippetService.new(@project, current_user, create_params).execute
+ create_params = snippet_params.merge(spammable_params)
- if @snippet.valid?
- respond_with(@snippet,
- location: namespace_project_snippet_path(@project.namespace,
- @project, @snippet))
- else
- render :new
- end
- end
+ @snippet = CreateSnippetService.new(@project, current_user, create_params).execute
- def edit
+ recaptcha_check_with_fallback { render :new }
end
def update
- UpdateSnippetService.new(project, current_user, @snippet,
- snippet_params).execute
- respond_with(@snippet,
- location: namespace_project_snippet_path(@project.namespace,
- @project, @snippet))
+ update_params = snippet_params.merge(spammable_params)
+
+ UpdateSnippetService.new(project, current_user, @snippet, update_params).execute
+
+ recaptcha_check_with_fallback { render :edit }
end
def show
@@ -74,15 +67,6 @@ class Projects::SnippetsController < Projects::ApplicationController
redirect_to namespace_project_snippets_path(@project.namespace, @project)
end
- def raw
- send_data(
- @snippet.content,
- type: 'text/plain; charset=utf-8',
- disposition: 'inline',
- filename: @snippet.sanitized_file_name
- )
- end
-
protected
def snippet
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index cb3ed0f6f9c..4f094146348 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -15,10 +15,10 @@ class Projects::TreeController < Projects::ApplicationController
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
- redirect_to(
+ return redirect_to(
namespace_project_blob_path(@project.namespace, @project,
File.join(@ref, @path))
- ) and return
+ )
elsif @path.present?
return render_404
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index b169d993688..2d26718873f 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,6 +1,7 @@
class SnippetsController < ApplicationController
include ToggleAwardEmoji
include SpammableActions
+ include SnippetsActions
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
@@ -22,7 +23,7 @@ class SnippetsController < ApplicationController
if params[:username].present?
@user = User.find_by(username: params[:username])
- render_404 and return unless @user
+ return render_404 unless @user
@snippets = SnippetsFinder.new.execute(current_user, {
filter: :by_user,
@@ -41,19 +42,19 @@ class SnippetsController < ApplicationController
end
def create
- create_params = snippet_params.merge(request: request)
- @snippet = CreateSnippetService.new(nil, current_user, create_params).execute
+ create_params = snippet_params.merge(spammable_params)
- respond_with @snippet.becomes(Snippet)
- end
+ @snippet = CreateSnippetService.new(nil, current_user, create_params).execute
- def edit
+ recaptcha_check_with_fallback { render :new }
end
def update
- UpdateSnippetService.new(nil, current_user, @snippet,
- snippet_params).execute
- respond_with @snippet.becomes(Snippet)
+ update_params = snippet_params.merge(spammable_params)
+
+ UpdateSnippetService.new(nil, current_user, @snippet, update_params).execute
+
+ recaptcha_check_with_fallback { render :edit }
end
def show
@@ -67,18 +68,9 @@ class SnippetsController < ApplicationController
redirect_to snippets_path
end
- def raw
- send_data(
- @snippet.content,
- type: 'text/plain; charset=utf-8',
- disposition: 'inline',
- filename: @snippet.sanitized_file_name
- )
- end
-
def download
send_data(
- @snippet.content,
+ convert_line_endings(@snippet.content),
type: 'text/plain; charset=utf-8',
filename: @snippet.sanitized_file_name
)
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 1576fc80a6b..206c92fe82a 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -16,6 +16,7 @@
# label_name: string
# sort: string
# non_archived: boolean
+# iids: integer[]
#
class IssuableFinder
NONE = '0'
@@ -40,6 +41,7 @@ class IssuableFinder
items = by_label(items)
items = by_due_date(items)
items = by_non_archived(items)
+ items = by_iids(items)
sort(items)
end
@@ -266,16 +268,11 @@ class IssuableFinder
end
def by_search(items)
- if search
- items =
- if search =~ iid_pattern
- items.where(iid: $~[:iid])
- else
- items.full_search(search)
- end
- end
+ search ? items.full_search(search) : items
+ end
- items
+ def by_iids(items)
+ params[:iids].present? ? items.where(iid: params[:iids]) : items
end
def sort(items)
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 707eddd4d29..f542f72a386 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -26,10 +26,6 @@ class IssuesFinder < IssuableFinder
IssuesFinder.not_restricted_by_confidentiality(current_user)
end
- def iid_pattern
- @iid_pattern ||= %r{\A#{Regexp.escape(Issue.reference_prefix)}(?<iid>\d+)\z}
- end
-
def self.not_restricted_by_confidentiality(user)
return Issue.where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 8b82255445e..b76ca389f38 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -20,14 +20,4 @@ class MergeRequestsFinder < IssuableFinder
def klass
MergeRequest
end
-
- private
-
- def iid_pattern
- @iid_pattern ||= %r{\A[
- #{Regexp.escape(MergeRequest.reference_prefix)}
- #{Regexp.escape(Issue.reference_prefix)}
- ](?<iid>\d+)\z
- }x
- end
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 2843ad96efa..a6d9e37ac76 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -1,4 +1,6 @@
module EmailsHelper
+ include AppearancesHelper
+
# Google Actions
# https://developers.google.com/gmail/markup/reference/go-to-action
def email_action(url)
@@ -49,4 +51,19 @@ module EmailsHelper
msg = "This link is valid for #{password_reset_token_valid_time}. "
msg << "After it expires, you can #{link_tag}."
end
+
+ def header_logo
+ if brand_item && brand_item.header_logo?
+ image_tag(
+ brand_item.header_logo,
+ style: 'height: 50px'
+ )
+ else
+ image_tag(
+ image_url('mailers/gitlab_header_logo.gif'),
+ size: "55x50",
+ alt: "GitLab"
+ )
+ end
+ end
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 0676767d910..dc5ae8edbb2 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -1,4 +1,8 @@
module NamespacesHelper
+ def namespace_id_from(params)
+ params.dig(:project, :namespace_id) || params[:namespace_id]
+ end
+
def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
groups = current_user.owned_groups + current_user.masters_groups
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index e21178c7377..c1523b4dabf 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -1,10 +1,4 @@
module NavHelper
- def page_sidebar_class
- if pinned_nav?
- "page-sidebar-expanded page-sidebar-pinned"
- end
- end
-
def page_gutter_class
if current_path?('merge_requests#show') ||
current_path?('merge_requests#diffs') ||
@@ -32,10 +26,6 @@ module NavHelper
class_name = ''
class_name << " with-horizontal-nav" if defined?(nav) && nav
- if pinned_nav?
- class_name << " header-sidebar-expanded header-sidebar-pinned"
- end
-
class_name
end
@@ -46,8 +36,4 @@ module NavHelper
def nav_control_class
"nav-control" if current_user
end
-
- def pinned_nav?
- cookies[:pin_nav] == 'true'
- end
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index dd0a4ea03f0..c3a08d76318 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -41,10 +41,6 @@ module PreferencesHelper
]
end
- def user_application_theme
- Gitlab::Themes.for_user(current_user).css_class
- end
-
def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 845f1a0e840..c52afd6db1c 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -3,6 +3,10 @@ module TodosHelper
@todos_pending_count ||= current_user.todos_pending_count
end
+ def todos_count_format(count)
+ count > 99 ? '99+' : count
+ end
+
def todos_done_count
@todos_done_count ||= current_user.todos_done_count
end
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index 9460a6cd2be..f9f45ab987b 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -22,8 +22,8 @@ module Emails
mail(bcc: recipients,
subject: pipeline_subject(status),
skip_premailer: true) do |format|
- format.html { render layout: false }
- format.text
+ format.html { render layout: 'mailer' }
+ format.text { render layout: 'mailer' }
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 8c1b076c2d7..e018f8e7c4e 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -62,33 +62,10 @@ module Ci
new_build.save
end
- def retry(build, user = nil)
- new_build = Ci::Build.create(
- ref: build.ref,
- tag: build.tag,
- options: build.options,
- commands: build.commands,
- tag_list: build.tag_list,
- project: build.project,
- pipeline: build.pipeline,
- name: build.name,
- allow_failure: build.allow_failure,
- stage: build.stage,
- stage_idx: build.stage_idx,
- trigger_request: build.trigger_request,
- yaml_variables: build.yaml_variables,
- when: build.when,
- user: user,
- environment: build.environment,
- status_event: 'enqueue'
- )
-
- MergeRequests::AddTodoWhenBuildFailsService
- .new(build.project, nil)
- .close(new_build)
-
- build.pipeline.mark_as_processable_after_stage(build.stage_idx)
- new_build
+ def retry(build, current_user)
+ Ci::RetryBuildService
+ .new(build.project, current_user)
+ .execute(build)
end
end
@@ -136,7 +113,7 @@ module Ci
project.builds_enabled? && commands.present? && manual? && skipped?
end
- def play(current_user = nil)
+ def play(current_user)
# Try to queue a current build
if self.enqueue
self.update(user: current_user)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index bbc358adb83..dc4590a9923 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -214,21 +214,17 @@ module Ci
def cancel_running
Gitlab::OptimisticLocking.retry_lock(
statuses.cancelable) do |cancelable|
- cancelable.each(&:cancel)
+ cancelable.find_each(&:cancel)
end
end
- def retry_failed(user)
- Gitlab::OptimisticLocking.retry_lock(
- builds.latest.failed_or_canceled) do |failed_or_canceled|
- failed_or_canceled.select(&:retryable?).each do |build|
- Ci::Build.retry(build, user)
- end
- end
+ def retry_failed(current_user)
+ Ci::RetryPipelineService.new(project, current_user)
+ .execute(self)
end
def mark_as_processable_after_stage(stage_idx)
- builds.skipped.where('stage_idx > ?', stage_idx).find_each(&:process)
+ builds.skipped.after_stage(stage_idx).find_each(&:process)
end
def latest?
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index ed1843ba005..07a086b0aca 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -22,8 +22,6 @@ module Ci
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) }
- after_save :tick_runner_queue, if: :form_editable_changed?
-
scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
@@ -40,6 +38,8 @@ module Ci
acts_as_taggable
+ after_destroy :cleanup_runner_queue
+
# Searches for runners matching the given query.
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
@@ -147,14 +147,14 @@ module Ci
private
- def runner_queue_key
- "runner:build_queue:#{self.token}"
+ def cleanup_runner_queue
+ Gitlab::Redis.with do |redis|
+ redis.del(runner_queue_key)
+ end
end
- def form_editable_changed?
- FORM_EDITABLE.any? do |editable|
- public_send("#{editable}_changed?")
- end
+ def runner_queue_key
+ "runner:build_queue:#{self.token}"
end
def tag_constraints
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 9547c57b2ae..99a6326309d 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -23,9 +23,6 @@ class CommitStatus < ActiveRecord::Base
where(id: max_id.group(:name, :commit_id))
end
- scope :retried, -> { where.not(id: latest) }
- scope :ordered, -> { order(:name) }
-
scope :failed_but_allowed, -> do
where(allow_failure: true, status: [:failed, :canceled])
end
@@ -36,8 +33,11 @@ class CommitStatus < ActiveRecord::Base
false, all_state_names - [:failed, :canceled])
end
+ scope :retried, -> { where.not(id: latest) }
+ scope :ordered, -> { order(:name) }
scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) }
scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
+ scope :after_stage, -> (index) { where('stage_idx > ?', index) }
state_machine :status do
event :enqueue do
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 5f53c48fc88..c9c6bd24d75 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -16,9 +16,9 @@ module Issuable
include TimeTrackable
# This object is used to gather issuable meta data for displaying
- # upvotes, downvotes and notes count for issues and merge requests
+ # upvotes, downvotes, notes and closing merge requests count for issues and merge requests
# lists avoiding n+1 queries and improving performance.
- IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count)
+ IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count, :merge_requests_count)
included do
cache_markdown_field :title, pipeline: :single_line
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index 423ae98a60e..107e6764ba2 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -13,7 +13,7 @@ module Spammable
attr_accessor :spam
attr_accessor :spam_log
- after_validation :check_for_spam, on: :create
+ after_validation :check_for_spam, on: [:create, :update]
cattr_accessor :spammable_attrs, instance_accessor: false do
[]
@@ -22,6 +22,10 @@ module Spammable
delegate :ip_address, :user_agent, to: :user_agent_detail, allow_nil: true
end
+ def submittable_as_spam_by?(current_user)
+ current_user && current_user.admin? && submittable_as_spam?
+ end
+
def submittable_as_spam?
if user_agent_detail
user_agent_detail.submittable? && current_application_settings.akismet_enabled
diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb
index ab597c37947..daafb137be4 100644
--- a/app/models/merge_requests_closing_issues.rb
+++ b/app/models/merge_requests_closing_issues.rb
@@ -4,4 +4,12 @@ class MergeRequestsClosingIssues < ActiveRecord::Base
validates :merge_request_id, uniqueness: { scope: :issue_id }, presence: true
validates :issue_id, presence: true
+
+ class << self
+ def count_for_collection(ids)
+ group(:issue_id).
+ where(issue_id: ids).
+ pluck('issue_id', 'COUNT(*) as count')
+ end
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index fc5b1a66910..411299eef63 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -552,7 +552,7 @@ class Project < ActiveRecord::Base
end
def check_limit
- unless creator.can_create_project? or namespace.kind == 'group'
+ unless creator.can_create_project? || namespace.kind == 'group'
projects_limit = creator.projects_limit
if projects_limit == 0
diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb
index a03605d01fb..86d271a3f69 100644
--- a/app/models/project_services/chat_message/base_message.rb
+++ b/app/models/project_services/chat_message/base_message.rb
@@ -30,5 +30,9 @@ module ChatMessage
def attachment_color
'#345'
end
+
+ def link(text, url)
+ "[#{text}](#{url})"
+ end
end
end
diff --git a/app/models/project_services/chat_message/build_message.rb b/app/models/project_services/chat_message/build_message.rb
index 53e35cb21bf..c776e0a20c4 100644
--- a/app/models/project_services/chat_message/build_message.rb
+++ b/app/models/project_services/chat_message/build_message.rb
@@ -7,7 +7,11 @@ module ChatMessage
attr_reader :project_name
attr_reader :project_url
attr_reader :user_name
+ attr_reader :user_url
attr_reader :duration
+ attr_reader :stage
+ attr_reader :build_id
+ attr_reader :build_name
def initialize(params)
@sha = params[:sha]
@@ -17,7 +21,11 @@ module ChatMessage
@project_url = params[:project_url]
@status = params[:commit][:status]
@user_name = params[:commit][:author_name]
+ @user_url = params[:commit][:author_url]
@duration = params[:commit][:duration]
+ @stage = params[:build_stage]
+ @build_name = params[:build_name]
+ @build_id = params[:build_id]
end
def pretext
@@ -35,7 +43,19 @@ module ChatMessage
private
def message
- "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{'second'.pluralize(duration)}"
+ "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_link} #{humanized_status} on build #{build_link} of stage #{stage} in #{duration} #{'second'.pluralize(duration)}"
+ end
+
+ def build_url
+ "#{project_url}/builds/#{build_id}"
+ end
+
+ def build_link
+ link(build_name, build_url)
+ end
+
+ def user_link
+ link(user_name, user_url)
end
def format(string)
@@ -64,11 +84,11 @@ module ChatMessage
end
def branch_link
- "[#{ref}](#{branch_url})"
+ link(ref, branch_url)
end
def project_link
- "[#{project_name}](#{project_url})"
+ link(project_name, project_url)
end
def commit_url
@@ -76,7 +96,7 @@ module ChatMessage
end
def commit_link
- "[#{Commit.truncate_sha(sha)}](#{commit_url})"
+ link(Commit.truncate_sha(sha), commit_url)
end
end
end
diff --git a/app/models/project_services/chat_message/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb
index 14fd64e5332..b96aca47e65 100644
--- a/app/models/project_services/chat_message/issue_message.rb
+++ b/app/models/project_services/chat_message/issue_message.rb
@@ -55,11 +55,11 @@ module ChatMessage
end
def project_link
- "[#{project_name}](#{project_url})"
+ link(project_name, project_url)
end
def issue_link
- "[#{issue_title}](#{issue_url})"
+ link(issue_title, issue_url)
end
def issue_title
diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb
index ab5e8b24167..5e5efca7bec 100644
--- a/app/models/project_services/chat_message/merge_message.rb
+++ b/app/models/project_services/chat_message/merge_message.rb
@@ -42,7 +42,7 @@ module ChatMessage
end
def project_link
- "[#{project_name}](#{project_url})"
+ link(project_name, project_url)
end
def merge_request_message
@@ -50,7 +50,7 @@ module ChatMessage
end
def merge_request_link
- "[merge request !#{merge_request_id}](#{merge_request_url})"
+ link("merge request !#{merge_request_id}", merge_request_url)
end
def merge_request_url
diff --git a/app/models/project_services/chat_message/note_message.rb b/app/models/project_services/chat_message/note_message.rb
index ca1d7207034..552113bac29 100644
--- a/app/models/project_services/chat_message/note_message.rb
+++ b/app/models/project_services/chat_message/note_message.rb
@@ -3,10 +3,9 @@ module ChatMessage
attr_reader :message
attr_reader :user_name
attr_reader :project_name
- attr_reader :project_link
+ attr_reader :project_url
attr_reader :note
attr_reader :note_url
- attr_reader :title
def initialize(params)
params = HashWithIndifferentAccess.new(params)
@@ -69,15 +68,15 @@ module ChatMessage
end
def description_message
- [{ text: format(@note), color: attachment_color }]
+ [{ text: format(note), color: attachment_color }]
end
def project_link
- "[#{@project_name}](#{@project_url})"
+ link(project_name, project_url)
end
def commented_on_message(target, title)
- @message = "#{@user_name} [commented on #{target}](#{@note_url}) in #{project_link}: *#{title}*"
+ @message = "#{user_name} #{link('commented on ' + target, note_url)} in #{project_link}: *#{title}*"
end
end
end
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index 942ec9371e5..1ad9efac196 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -52,7 +52,7 @@ class DroneCiService < CiService
response = HTTParty.get(commit_status_path(sha, ref), verify: enable_ssl_verification)
status =
- if response.code == 200 and response['status']
+ if response.code == 200 && response['status']
case response['status']
when 'killed'
:canceled
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 5d93064f9b3..5d6862d9faa 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -96,7 +96,7 @@ class IrkerService < Service
rescue URI::InvalidURIError
end
- unless uri.present? and default_irc_uri.nil?
+ unless uri.present? && default_irc_uri.nil?
begin
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb
index 9bb456eee24..25b5d777641 100644
--- a/app/models/project_snippet.rb
+++ b/app/models/project_snippet.rb
@@ -9,8 +9,4 @@ class ProjectSnippet < Snippet
participant :author
participant :notes_with_associations
-
- def check_for_spam?
- super && project.public?
- end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index ad997ce2b13..f614eb66e1f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -21,7 +21,6 @@ class User < ActiveRecord::Base
default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false
default_value_for :hide_no_password, false
- default_value_for :theme_id, gitlab_config.default_theme
attr_encrypted :otp_secret,
key: Gitlab::Application.secrets.otp_key_base,
diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb
index a559d0850c4..69bf693de8d 100644
--- a/app/serializers/analytics_stage_entity.rb
+++ b/app/serializers/analytics_stage_entity.rb
@@ -2,6 +2,7 @@ class AnalyticsStageEntity < Grape::Entity
include EntityDateHelper
expose :title
+ expose :legend
expose :description
expose :median, as: :value do |stage|
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 1a2bad77a02..fa45506317e 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -1,4 +1,5 @@
class BaseService
+ include Gitlab::Allowable
include Gitlab::CurrentSettings
attr_accessor :project, :current_user, :params
@@ -7,10 +8,6 @@ class BaseService
@project, @current_user, @params = project, user, params.dup
end
- def can?(object, action, subject)
- Ability.allowed?(object, action, subject)
- end
-
def notification_service
NotificationService.new
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index e3bc9847200..38a85e9fc42 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -59,7 +59,8 @@ module Ci
private
def skip_ci?
- pipeline.git_commit_message =~ /\[(ci skip|skip ci)\]/i if pipeline.git_commit_message
+ return false unless pipeline.git_commit_message
+ pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i
end
def commit
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
new file mode 100644
index 00000000000..4b47ee489cf
--- /dev/null
+++ b/app/services/ci/retry_build_service.rb
@@ -0,0 +1,42 @@
+module Ci
+ class RetryBuildService < ::BaseService
+ CLONE_ATTRIBUTES = %i[pipeline ref tag options commands tag_list name
+ allow_failure stage stage_idx trigger_request
+ yaml_variables when environment coverage_regex]
+ .freeze
+
+ REJECT_ATTRIBUTES = %i[id status user token coverage trace runner
+ artifacts_file artifacts_metadata artifacts_size
+ created_at updated_at started_at finished_at
+ queued_at erased_by erased_at].freeze
+
+ IGNORE_ATTRIBUTES = %i[trace type lock_version project target_url
+ deploy job_id description].freeze
+
+ def execute(build)
+ reprocess(build).tap do |new_build|
+ build.pipeline.mark_as_processable_after_stage(build.stage_idx)
+
+ new_build.enqueue!
+
+ MergeRequests::AddTodoWhenBuildFailsService
+ .new(project, current_user)
+ .close(new_build)
+ end
+ end
+
+ def reprocess(build)
+ unless can?(current_user, :update_build, build)
+ raise Gitlab::Access::AccessDeniedError
+ end
+
+ attributes = CLONE_ATTRIBUTES.map do |attribute|
+ [attribute, build.send(attribute)]
+ end
+
+ attributes.push([:user, current_user])
+
+ project.builds.create(Hash[attributes])
+ end
+ end
+end
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
new file mode 100644
index 00000000000..2c5e130e5aa
--- /dev/null
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -0,0 +1,22 @@
+module Ci
+ class RetryPipelineService < ::BaseService
+ def execute(pipeline)
+ unless can?(current_user, :update_pipeline, pipeline)
+ raise Gitlab::Access::AccessDeniedError
+ end
+
+ pipeline.builds.failed_or_canceled.find_each do |build|
+ next unless build.retryable?
+
+ Ci::RetryBuildService.new(project, current_user)
+ .reprocess(build)
+ end
+
+ MergeRequests::AddTodoWhenBuildFailsService
+ .new(project, current_user)
+ .close_all(pipeline)
+
+ pipeline.process!
+ end
+ end
+end
diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb
new file mode 100644
index 00000000000..450ee7da1c9
--- /dev/null
+++ b/app/services/ci/update_runner_service.rb
@@ -0,0 +1,15 @@
+module Ci
+ class UpdateRunnerService
+ attr_reader :runner
+
+ def initialize(runner)
+ @runner = runner
+ end
+
+ def update(params)
+ runner.update(params).tap do |updated|
+ runner.tick_runner_queue if updated
+ end
+ end
+ end
+end
diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb
index 14f5ba064ff..40286dbf3bf 100644
--- a/app/services/create_snippet_service.rb
+++ b/app/services/create_snippet_service.rb
@@ -1,7 +1,8 @@
class CreateSnippetService < BaseService
+ include SpamCheckService
+
def execute
- request = params.delete(:request)
- api = params.delete(:api)
+ filter_spam_check_params
snippet = if project
project.snippets.build(params)
@@ -15,10 +16,11 @@ class CreateSnippetService < BaseService
end
snippet.author = current_user
- snippet.spam = SpamService.new(snippet, request).check(api)
+
+ spam_check(snippet, current_user)
if snippet.save
- UserAgentDetailService.new(snippet, request).create
+ UserAgentDetailService.new(snippet, @request).create
end
snippet
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 6ba868df04d..af6da5b9d56 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -55,7 +55,7 @@ module Files
file_path = action[:file_path]
file_path = action[:previous_path] if action[:action] == :move
- blob = repository.blob_at_branch(params[:branch_name], file_path)
+ blob = repository.blob_at_branch(params[:branch], file_path)
unless blob
raise_error("File to be #{action[:action]}d `#{file_path}` does not exist.")
@@ -89,7 +89,7 @@ module Files
def validate_create(action)
return if project.empty_repo?
- if repository.blob_at_branch(params[:branch_name], action[:file_path])
+ if repository.blob_at_branch(params[:branch], action[:file_path])
raise_error("Your changes could not be committed because a file with the name `#{action[:file_path]}` already exists.")
end
end
@@ -102,14 +102,14 @@ module Files
raise_error("You must supply the original file path when moving file `#{action[:file_path]}`.")
end
- blob = repository.blob_at_branch(params[:branch_name], action[:file_path])
+ blob = repository.blob_at_branch(params[:branch], action[:file_path])
if blob
raise_error("Move destination `#{action[:file_path]}` already exists.")
end
if action[:content].nil?
- blob = repository.blob_at_branch(params[:branch_name], action[:previous_path])
+ blob = repository.blob_at_branch(params[:branch], action[:previous_path])
blob.load_all_data!(repository) if blob.truncated?
params[:actions][index][:content] = blob.data
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 5f3ced49665..9500faf2862 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -191,14 +191,12 @@ class IssuableBaseService < BaseService
# To be overridden by subclasses
end
- def after_update(issuable)
+ def before_update(issuable)
# To be overridden by subclasses
end
- def update_issuable(issuable, attributes)
- issuable.with_transaction_returning_status do
- issuable.update(attributes.merge(updated_by: current_user))
- end
+ def after_update(issuable)
+ # To be overridden by subclasses
end
def update(issuable)
@@ -212,16 +210,22 @@ class IssuableBaseService < BaseService
label_ids = process_label_ids(params, existing_label_ids: issuable.label_ids)
params[:label_ids] = label_ids if labels_changing?(issuable.label_ids, label_ids)
- if params.present? && update_issuable(issuable, params)
- # We do not touch as it will affect a update on updated_at field
- ActiveRecord::Base.no_touching do
- handle_common_system_notes(issuable, old_labels: old_labels)
- end
+ if params.present?
+ issuable.assign_attributes(params.merge(updated_by: current_user))
+
+ before_update(issuable)
- handle_changes(issuable, old_labels: old_labels, old_mentioned_users: old_mentioned_users)
- after_update(issuable)
- issuable.create_new_cross_references!(current_user)
- execute_hooks(issuable, 'update')
+ if issuable.with_transaction_returning_status { issuable.save }
+ # We do not touch as it will affect a update on updated_at field
+ ActiveRecord::Base.no_touching do
+ handle_common_system_notes(issuable, old_labels: old_labels)
+ end
+
+ handle_changes(issuable, old_labels: old_labels, old_mentioned_users: old_mentioned_users)
+ after_update(issuable)
+ issuable.create_new_cross_references!(current_user)
+ execute_hooks(issuable, 'update')
+ end
end
issuable
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 961605a1005..366b3572738 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -1,10 +1,9 @@
module Issues
class CreateService < Issues::BaseService
+ include SpamCheckService
+
def execute
- @request = params.delete(:request)
- @api = params.delete(:api)
- @recaptcha_verified = params.delete(:recaptcha_verified)
- @spam_log_id = params.delete(:spam_log_id)
+ filter_spam_check_params
issue_attributes = params.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions)
@issue = BuildService.new(project, current_user, issue_attributes).execute
@@ -12,14 +11,8 @@ module Issues
create(@issue)
end
- def before_create(issuable)
- if @recaptcha_verified
- spam_log = current_user.spam_logs.find_by(id: @spam_log_id, title: issuable.title)
- spam_log&.update!(recaptcha_verified: true)
- else
- issuable.spam = spam_service.check(@api)
- issuable.spam_log = spam_service.spam_log
- end
+ def before_create(issue)
+ spam_check(issue, current_user)
end
def after_create(issuable)
@@ -42,10 +35,6 @@ module Issues
private
- def spam_service
- @spam_service ||= SpamService.new(@issue, @request)
- end
-
def user_agent_detail_service
UserAgentDetailService.new(@issue, @request)
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 78cbf94ec69..22e32b13259 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -1,9 +1,17 @@
module Issues
class UpdateService < Issues::BaseService
+ include SpamCheckService
+
def execute(issue)
+ filter_spam_check_params
+
update(issue)
end
+ def before_update(issue)
+ spam_check(issue, current_user)
+ end
+
def handle_changes(issue, old_labels: [], old_mentioned_users: [])
if has_changes?(issue, old_labels: old_labels)
todo_service.mark_pending_todos_as_done(issue, current_user)
diff --git a/app/services/merge_requests/add_todo_when_build_fails_service.rb b/app/services/merge_requests/add_todo_when_build_fails_service.rb
index 12a8415d9a5..727768b1a39 100644
--- a/app/services/merge_requests/add_todo_when_build_fails_service.rb
+++ b/app/services/merge_requests/add_todo_when_build_fails_service.rb
@@ -18,5 +18,11 @@ module MergeRequests
todo_service.merge_request_build_retried(merge_request)
end
end
+
+ def close_all(pipeline)
+ pipeline_merge_requests(pipeline) do |merge_request|
+ todo_service.merge_request_build_retried(merge_request)
+ end
+ end
end
end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index f4d52e3ebbd..9d4739e37bb 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -2,18 +2,14 @@ module MergeRequests
class BuildService < MergeRequests::BaseService
def execute
self.merge_request = MergeRequest.new(params)
- merge_request.can_be_created = true
merge_request.compare_commits = []
merge_request.source_project = find_source_project
merge_request.target_project = find_target_project
merge_request.target_branch = find_target_branch
+ merge_request.can_be_created = branches_valid? && source_branch_specified? && target_branch_specified?
- if branches_specified? && branches_valid?
- compare_branches
- assign_title_and_description
- else
- merge_request.can_be_created = false
- end
+ compare_branches if branches_present?
+ assign_title_and_description if merge_request.can_be_created
merge_request
end
@@ -37,11 +33,17 @@ module MergeRequests
target_branch || target_project.default_branch
end
- def branches_specified?
- params[:source_branch] && params[:target_branch]
+ def source_branch_specified?
+ params[:source_branch].present?
+ end
+
+ def target_branch_specified?
+ params[:target_branch].present?
end
def branches_valid?
+ return false unless source_branch_specified? || target_branch_specified?
+
validate_branches
errors.blank?
end
@@ -55,8 +57,10 @@ module MergeRequests
target_branch
)
- merge_request.compare_commits = compare.commits
- merge_request.compare = compare
+ if compare
+ merge_request.compare_commits = compare.commits
+ merge_request.compare = compare
+ end
end
def validate_branches
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 177b714b734..3da1b657a41 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -18,7 +18,7 @@ module MergeRequests
@source = find_merge_source
unless @source
- log_merge_error('No source for merge', save_message_on_model: true)
+ return log_merge_error('No source for merge', save_message_on_model: true)
end
merge_request.in_locked_state do
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index a08c6fcd94b..9716a1780a9 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -17,8 +17,6 @@ module Projects
def execute
return false unless can?(current_user, :remove_project, project)
- project.team.truncate
-
repo_path = project.path_with_namespace
wiki_path = repo_path + '.wiki'
@@ -30,6 +28,7 @@ module Projects
Projects::UnlinkForkService.new(project, current_user).execute
Project.transaction do
+ project.team.truncate
project.destroy!
unless remove_registry_tags
diff --git a/app/services/projects/upload_service.rb b/app/services/projects/upload_service.rb
index 012e82a7704..be34d4fa9b8 100644
--- a/app/services/projects/upload_service.rb
+++ b/app/services/projects/upload_service.rb
@@ -5,7 +5,7 @@ module Projects
end
def execute
- return nil unless @file and @file.size <= max_attachment_size
+ return nil unless @file && @file.size <= max_attachment_size
uploader = FileUploader.new(@project)
uploader.store!(@file)
diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb
new file mode 100644
index 00000000000..023e0824e85
--- /dev/null
+++ b/app/services/spam_check_service.rb
@@ -0,0 +1,24 @@
+# SpamCheckService
+#
+# Provide helper methods for checking if a given spammable object has
+# potential spam data.
+#
+# Dependencies:
+# - params with :request
+#
+module SpamCheckService
+ def filter_spam_check_params
+ @request = params.delete(:request)
+ @api = params.delete(:api)
+ @recaptcha_verified = params.delete(:recaptcha_verified)
+ @spam_log_id = params.delete(:spam_log_id)
+ end
+
+ def spam_check(spammable, user)
+ spam_service = SpamService.new(spammable, @request)
+
+ spam_service.when_recaptcha_verified(@recaptcha_verified, @api) do
+ user.spam_logs.find_by(id: @spam_log_id)&.update!(recaptcha_verified: true)
+ end
+ end
+end
diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb
index 024a7c19d33..3e65b7d31a3 100644
--- a/app/services/spam_service.rb
+++ b/app/services/spam_service.rb
@@ -17,15 +17,6 @@ class SpamService
end
end
- def check(api = false)
- return false unless request && check_for_spam?
-
- return false unless akismet.is_spam?
-
- create_spam_log(api)
- true
- end
-
def mark_as_spam!
return false unless spammable.submittable_as_spam?
@@ -36,8 +27,30 @@ class SpamService
end
end
+ def when_recaptcha_verified(recaptcha_verified, api = false)
+ # In case it's a request which is already verified through recaptcha, yield
+ # block.
+ if recaptcha_verified
+ yield
+ else
+ # Otherwise, it goes to Akismet and check if it's a spam. If that's the
+ # case, it assigns spammable record as "spam" and create a SpamLog record.
+ spammable.spam = check(api)
+ spammable.spam_log = spam_log
+ end
+ end
+
private
+ def check(api)
+ return false unless request && check_for_spam?
+
+ return false unless akismet.is_spam?
+
+ create_spam_log(api)
+ true
+ end
+
def akismet
@akismet ||= AkismetService.new(
spammable_owner,
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 8ab943f4639..ad86b4f9f42 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -170,16 +170,20 @@ class TodoService
# When user marks some todos as done
def mark_todos_as_done(todos, current_user)
- mark_todos_as_done_by_ids(todos.select(&:id), current_user)
+ update_todos_state_by_ids(todos.select(&:id), current_user, :done)
end
def mark_todos_as_done_by_ids(ids, current_user)
- todos = current_user.todos.where(id: ids)
+ update_todos_state_by_ids(ids, current_user, :done)
+ end
- # Only return those that are not really on that state
- marked_todos = todos.where.not(state: :done).update_all(state: :done)
- current_user.update_todos_count_cache
- marked_todos
+ # When user marks some todos as pending
+ def mark_todos_as_pending(todos, current_user)
+ update_todos_state_by_ids(todos.select(&:id), current_user, :pending)
+ end
+
+ def mark_todos_as_pending_by_ids(ids, current_user)
+ update_todos_state_by_ids(ids, current_user, :pending)
end
# When user marks an issue as todo
@@ -194,6 +198,15 @@ class TodoService
private
+ def update_todos_state_by_ids(ids, current_user, state)
+ todos = current_user.todos.where(id: ids)
+
+ # Only return those that are not really on that state
+ marked_todos = todos.where.not(state: state).update_all(state: state)
+ current_user.update_todos_count_cache
+ marked_todos
+ end
+
def create_todos(users, attributes)
Array(users).map do |user|
next if pending_todos(user, attributes).exists?
diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb
index a6bb36821c3..358bca73aec 100644
--- a/app/services/update_snippet_service.rb
+++ b/app/services/update_snippet_service.rb
@@ -1,4 +1,6 @@
class UpdateSnippetService < BaseService
+ include SpamCheckService
+
attr_accessor :snippet
def initialize(project, user, snippet, params)
@@ -9,7 +11,7 @@ class UpdateSnippetService < BaseService
def execute
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
-
+
if new_visibility && new_visibility.to_i != snippet.visibility_level
unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(snippet, new_visibility)
@@ -17,6 +19,10 @@ class UpdateSnippetService < BaseService
end
end
- snippet.update_attributes(params)
+ filter_spam_check_params
+ snippet.assign_attributes(params)
+ spam_check(snippet, current_user)
+
+ snippet.save
end
end
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index 2d11305be13..bc0653cb634 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -7,6 +7,10 @@ module Users
end
def execute(user, options = {})
+ unless current_user.admin? || current_user == user
+ raise Gitlab::Access::AccessDeniedError, "#{current_user} tried to destroy user #{user}!"
+ end
+
if !options[:delete_solo_owned_groups] && user.solo_owned_groups.present?
user.errors[:base] << 'You must transfer ownership or delete groups before you can remove user'
return user
diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml
index c4b748d0ab8..6c48328da4f 100644
--- a/app/views/admin/abuse_reports/index.html.haml
+++ b/app/views/admin/abuse_reports/index.html.haml
@@ -12,6 +12,7 @@
%th.wide Message
%th Action
= render @abuse_reports
+ = paginate @abuse_reports, theme: 'gitlab'
- else
.empty-state
.text-center
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 816035ec442..749c74b8110 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -192,7 +192,7 @@
= f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_pages_size, class: 'form-control'
- .help-block Zero for unlimited
+ .help-block 0 for unlimited
%fieldset
%legend Continuous Integration
@@ -525,7 +525,7 @@
= f.number_field :terminal_max_session_time, class: 'form-control'
.help-block
Maximum time for web terminal websocket connection (in seconds).
- Set to 0 for unlimited time.
+ 0 for unlimited.
.form-actions
= f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 4f982a6e369..ac36bb5bb17 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -35,7 +35,7 @@
.clearfix
%p
%i.fa.fa-exclamation-circle
- If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
+ If '[#{@concurrency} of #{@concurrency} busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%p
%i.fa.fa-exclamation-circle
If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab.
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 721bc77cc2f..d725e477044 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -56,7 +56,7 @@
= submit_tag 'Search', class: 'btn'
.pull-right.light
- Runners with last contact less than a minute ago: #{@active_runners_cnt}
+ Runners with last contact more than a minute ago: #{@active_runners_cnt}
%br
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 605bfd0cf8d..a3993d5ef16 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -1,28 +1,33 @@
%li{ class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } }
- = author_avatar(todo, size: 40)
+ .todo-avatar
+ = author_avatar(todo, size: 40)
.todo-item.todo-block
.todo-title.title
- unless todo.build_failed? || todo.unmergeable?
= todo_target_state_pill(todo)
- %span.author-name
+ .title-item.author-name
- if todo.author
= link_to_author(todo)
- else
(removed)
- %span.action-name
+ .title-item.action-name
= todo_action_name(todo)
- %span.todo-label
+ .title-item.todo-label
- if todo.target
= todo_target_link(todo)
- else
(removed)
- &middot; #{time_ago_with_tooltip(todo.created_at)}
- = todo_due_date(todo)
+ .title-item
+ &middot;
+
+ .title-item
+ #{time_ago_with_tooltip(todo.created_at)}
+ = todo_due_date(todo)
.todo-body
.todo-note
@@ -31,6 +36,9 @@
- if todo.pending?
.todo-actions
- = link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading done-todo' do
+ = link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading js-done-todo' do
Done
= icon('spinner spin')
+ = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'btn btn-loading js-undo-todo hidden' do
+ Undo
+ = icon('spinner spin')
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 64ca3c32e01..efd13aabf20 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -3,11 +3,9 @@
.event-title
%span.author_name= link_to_author event
%span.pushed #{event.action_name} #{event.ref_type}
- - if event.rm_ref?
- %strong= event.ref_name
- - else
- %strong
- = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title)
+ %strong
+ - commits_link = namespace_project_commits_path(project.namespace, project, event.ref_name)
+ = link_to_if project.repository.branch_exists?(event.ref_name), event.ref_name, commits_link
= render "events/event_scope", event: event
diff --git a/app/views/groups/_head.html.haml b/app/views/groups/_head.html.haml
new file mode 100644
index 00000000000..6b296ea8dea
--- /dev/null
+++ b/app/views/groups/_head.html.haml
@@ -0,0 +1,19 @@
+= content_for :sub_nav do
+ .scrolling-tabs-container.sub-nav-scroll
+ = render 'shared/nav_scroll'
+ .nav-links.sub-nav.scrolling-tabs
+ %ul{ class: container_class }
+ = nav_link(path: 'groups#show', html_options: { class: 'home' }) do
+ = link_to group_path(@group), title: 'Group Home' do
+ %span
+ Home
+
+ = nav_link(path: 'groups#activity') do
+ = link_to activity_group_path(@group), title: 'Activity' do
+ %span
+ Activity
+
+ = nav_link(path: 'group_members#index') do
+ = link_to group_group_members_path(@group), title: 'Members' do
+ %span
+ Members
diff --git a/app/views/groups/_head_issues.html.haml b/app/views/groups/_head_issues.html.haml
new file mode 100644
index 00000000000..d554bc23743
--- /dev/null
+++ b/app/views/groups/_head_issues.html.haml
@@ -0,0 +1,19 @@
+= content_for :sub_nav do
+ .scrolling-tabs-container.sub-nav-scroll
+ = render 'shared/nav_scroll'
+ .nav-links.sub-nav.scrolling-tabs
+ %ul{ class: container_class }
+ = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
+ = link_to issues_group_path(@group), title: 'List' do
+ %span
+ List
+
+ = nav_link(path: 'labels#index') do
+ = link_to group_labels_path(@group), title: 'Labels' do
+ %span
+ Labels
+
+ = nav_link(path: 'milestones#index') do
+ = link_to group_milestones_path(@group), title: 'Milestones' do
+ %span
+ Milestones
diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml
index aaad265b3ee..d7375b23524 100644
--- a/app/views/groups/activity.html.haml
+++ b/app/views/groups/activity.html.haml
@@ -2,7 +2,8 @@
- if current_user
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
-- page_title "Activity"
+- page_title "Activity"
+= render 'groups/head'
%section.activities
= render 'activities'
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 2e4e4511bb6..8cb56443191 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,4 +1,5 @@
- page_title "Members"
+= render 'groups/head'
.project-members-page.prepend-top-default
%h4
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 83edb719692..939bddf3fe9 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,4 +1,5 @@
- page_title "Issues"
+= render "head_issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues")
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 45325d6bc4b..2bc00fb16c8 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,4 +1,5 @@
- page_title 'Labels'
+= render "groups/head_issues"
.top-area.adjust
.nav-text
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index cd5388fe402..644895c56a1 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,4 +1,5 @@
- page_title "Milestones"
+= render "groups/head_issues"
.top-area
= render 'shared/milestones_filter'
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index b040f404ac4..3d7b469660a 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -4,6 +4,7 @@
- if current_user
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
+= render 'groups/head'
= render 'groups/home_panel'
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 54d02ee8e4b..a35a918d501 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,21 +1,4 @@
-.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
- .sidebar-wrapper.nicescroll
- .sidebar-action-buttons
- .nav-header-btn.toggle-nav-collapse{ title: "Open/Close" }
- %span.sr-only Toggle navigation
- = icon('bars')
-
- %div{ class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: { placement: 'right', container: 'body' } }
- %span.sr-only Toggle navigation pinning
- = icon('fw thumb-tack')
-
- - if defined?(sidebar) && sidebar
- = render "layouts/nav/#{sidebar}"
- - elsif current_user
- = render 'layouts/nav/dashboard'
- - else
- = render 'layouts/nav/explore'
-
+.page-with-sidebar{ class: page_gutter_class }
- if defined?(nav) && nav
.layout-nav
.container-fluid
diff --git a/app/views/layouts/_recaptcha_verification.html.haml b/app/views/layouts/_recaptcha_verification.html.haml
new file mode 100644
index 00000000000..77c77dc6754
--- /dev/null
+++ b/app/views/layouts/_recaptcha_verification.html.haml
@@ -0,0 +1,23 @@
+- humanized_resource_name = spammable.class.model_name.human.downcase
+- resource_name = spammable.class.model_name.singular
+
+%h3.page-title
+ Anti-spam verification
+%hr
+
+%p
+ #{"We detected potential spam in the #{humanized_resource_name}. Please solve the reCAPTCHA to proceed."}
+
+= form_for form do |f|
+ .recaptcha
+ - params[resource_name].each do |field, value|
+ = hidden_field(resource_name, field, value: value)
+ = hidden_field_tag(:spam_log_id, spammable.spam_log.id)
+ = hidden_field_tag(:recaptcha_verification, true)
+ = recaptcha_tags
+
+ -# Yields a block with given extra params.
+ = yield
+
+ .row-content-block.footer-block
+ = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create'
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 248d439cd05..19bd9b6d5c9 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en", class: "#{page_class}" }
= render "layouts/head"
- %body{ class: "#{user_application_theme}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } }
+ %body{ data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } }
= Gon::Base.render_data
= render "layouts/header/default", title: header_title
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 59082ce5fd5..0b8388cbff3 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -2,9 +2,15 @@
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
.header-content
- %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" }
- %span.sr-only Toggle navigation
- = icon('bars')
+ .dropdown.global-dropdown
+ %button.global-dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
+ %span.sr-only Toggle navigation
+ = icon('bars')
+ .dropdown-menu-nav.global-dropdown-menu
+ - if current_user
+ = render 'layouts/nav/dashboard'
+ - else
+ = render 'layouts/nav/explore'
%button.navbar-toggle{ type: 'button' }
%span.sr-only Toggle navigation
= icon('ellipsis-v')
@@ -29,7 +35,7 @@
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
- = todos_pending_count
+ = todos_count_format(todos_pending_count)
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, title: 'Sherlock Transactions',
@@ -55,7 +61,6 @@
%div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
-
%h1.title= title
.header-logo
diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml
new file mode 100644
index 00000000000..53268cc22f8
--- /dev/null
+++ b/app/views/layouts/mailer.html.haml
@@ -0,0 +1,72 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+%html{ lang: "en" }
+ %head
+ %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
+ %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
+ %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
+ %title= message.subject
+ :css
+ /* CLIENT-SPECIFIC STYLES */
+ body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
+ table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
+ img { -ms-interpolation-mode: bicubic; }
+
+ /* iOS BLUE LINKS */
+ a[x-apple-data-detectors] {
+ color: inherit !important;
+ text-decoration: none !important;
+ font-size: inherit !important;
+ font-family: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+ }
+
+ /* ANDROID MARGIN HACK */
+ body { margin:0 !important; }
+ div[style*="margin: 16px 0"] { margin:0 !important; }
+
+ @media only screen and (max-width: 639px) {
+ body, #body {
+ min-width: 320px !important;
+ }
+ table.wrapper {
+ width: 100% !important;
+ min-width: 320px !important;
+ }
+ table.wrapper > tbody > tr > td {
+ border-left: 0 !important;
+ border-right: 0 !important;
+ border-radius: 0 !important;
+ padding-left: 10px !important;
+ padding-right: 10px !important;
+ }
+ }
+ %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
+ %tbody
+ %tr.line
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
+ %tr.header
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ = header_logo
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
+ %tbody
+ = yield
+
+ %tr.footer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
+ %div
+ %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
+ &middot;
+ %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
+ %div
+ You're receiving this email because of your account on
+ = succeed "." do
+ %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
diff --git a/app/views/layouts/mailer.text.haml b/app/views/layouts/mailer.text.haml
new file mode 100644
index 00000000000..6a9c6ced9cc
--- /dev/null
+++ b/app/views/layouts/mailer.text.haml
@@ -0,0 +1,5 @@
+= yield
+
+You're receiving this email because of your account on #{Gitlab.config.gitlab.host}.
+Manage all notifications: #{profile_notifications_url}
+Help: #{help_url}
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 205d23178d2..5d4178f03d7 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,41 +1,39 @@
-.nav-sidebar
- %ul.nav
- = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
- = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
- %span
- Projects
- = nav_link(path: 'dashboard#activity') do
- = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
- %span
- 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
- = link_to dashboard_groups_path, title: 'Groups' do
- %span
- Groups
- = nav_link(controller: 'dashboard/milestones') do
- = link_to dashboard_milestones_path, title: 'Milestones' do
- %span
- Milestones
- = nav_link(path: 'dashboard#issues') do
- = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
- %span
- Issues
- %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
- = nav_link(path: 'dashboard#merge_requests') do
- = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
- %span
- Merge Requests
- %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
- = nav_link(controller: 'dashboard/snippets') do
- = link_to dashboard_snippets_path, title: 'Snippets' do
+%ul
+ = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
+ = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
+ %span
+ Projects
+ = nav_link(path: 'dashboard#activity') do
+ = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
+ %span
+ Activity
+ - if koding_enabled?
+ = nav_link(controller: :koding) do
+ = link_to koding_path, title: 'Koding' do
%span
- Snippets
-
- = link_to help_path, title: 'About GitLab CE', class: 'about-gitlab' do
+ Koding
+ = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
+ = link_to dashboard_groups_path, title: 'Groups' do
+ %span
+ Groups
+ = nav_link(controller: 'dashboard/milestones') do
+ = link_to dashboard_milestones_path, title: 'Milestones' do
+ %span
+ Milestones
+ = nav_link(path: 'dashboard#issues') do
+ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
+ %span
+ Issues
+ (#{number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))})
+ = nav_link(path: 'dashboard#merge_requests') do
+ = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
+ %span
+ Merge Requests
+ (#{number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))})
+ = nav_link(controller: 'dashboard/snippets') do
+ = link_to dashboard_snippets_path, title: 'Snippets' do
%span
- About GitLab CE
+ Snippets
+ %li.divider
+ %li
+ = link_to "About GitLab CE", help_path, title: 'About GitLab CE', class: 'about-gitlab'
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index e5bda7b3a6f..3a1fcd00e9c 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-sidebar
+%ul
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do
%span
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index f3539fd372d..e0742d70fac 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -5,23 +5,11 @@
.fade-right
= icon('angle-right')
%ul.nav-links.scrolling-tabs
- = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
+ = nav_link(path: ['groups#show', 'groups#activity', 'group_members#index'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'Home' do
%span
Group
- = nav_link(path: 'groups#activity') do
- = link_to activity_group_path(@group), title: 'Activity' do
- %span
- Activity
- = nav_link(controller: [:group, :labels]) do
- = link_to group_labels_path(@group), title: 'Labels' do
- %span
- Labels
- = nav_link(controller: [:group, :milestones]) do
- = link_to group_milestones_path(@group), title: 'Milestones' do
- %span
- Milestones
- = nav_link(path: 'groups#issues') do
+ = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
= link_to issues_group_path(@group), title: 'Issues' do
%span
Issues
@@ -33,7 +21,3 @@
Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
%span.badge.count= number_with_delimiter(merge_requests.count)
- = nav_link(controller: [:group_members]) do
- = link_to group_group_members_path(@group), title: 'Members' do
- %span
- Members
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index d9ebbaa2704..85a1aea3a61 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -1,179 +1,109 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-%html{ lang: "en" }
- %head
- %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
- %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
- %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
- %title= message.subject
- :css
- /* CLIENT-SPECIFIC STYLES */
- body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
- table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
- img { -ms-interpolation-mode: bicubic; }
-
- /* iOS BLUE LINKS */
- a[x-apple-data-detectors] {
- color: inherit !important;
- text-decoration: none !important;
- font-size: inherit !important;
- font-family: inherit !important;
- font-weight: inherit !important;
- line-height: inherit !important;
- }
-
- /* ANDROID MARGIN HACK */
- body { margin:0 !important; }
- div[style*="margin: 16px 0"] { margin:0 !important; }
-
- @media only screen and (max-width: 639px) {
- body, #body {
- min-width: 320px !important;
- }
- table.wrapper {
- width: 100% !important;
- min-width: 320px !important;
- }
- table.wrapper > tbody > tr > td {
- border-left: 0 !important;
- border-right: 0 !important;
- border-radius: 0 !important;
- padding-left: 10px !important;
- padding-right: 10px !important;
- }
- }
- %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
+%tr.alert
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
- %tr.line
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
- %tr.header
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
+ %img{ alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
+ Your pipeline has failed.
+%tr.spacer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
+ &nbsp;
+%tr.section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
+ - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
+ - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
+ %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
+ = namespace_name
+ \/
+ %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
+ = @project.name
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
+ = @pipeline.ref
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
- %tbody
- %tr.alert
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
- %img{ alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- Your pipeline has failed.
- %tr.spacer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
- &nbsp;
- %tr.section
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
- %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
- = namespace_name
- \/
- %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
- = @project.name
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
- = @pipeline.ref
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
- = @pipeline.short_sha
- - if @merge_request
- in
- %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
- = @merge_request.to_reference
- .commit{ style: "color:#5c5c5c;font-weight:300;" }
- = @pipeline.git_commit_message.truncate(50)
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- - commit = @pipeline.commit
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- - if commit.author
- %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
- = commit.author.name
- - else
- %span
- = commit.author_name
- %tr.spacer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
- &nbsp;
- - failed = @pipeline.statuses.latest.failed
- %tr.pre-section
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;" }
- Pipeline
- %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
- = "\##{@pipeline.id}"
- had
- = failed.size
- failed
- #{'build'.pluralize(failed.size)}.
- %tr.warning
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" }
- Logs may contain sensitive data. Please consider before forwarding this email.
- %tr.section
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" }
- %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" }
- %tbody
- - failed.each do |build|
- %tr.build-state
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;" }
- %img{ alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" }
- = build.stage
- %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
- = render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build
- %tr.build-log
- - if build.has_trace?
- %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" }
- %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" }
- = build.trace_html(last_lines: 10).html_safe
- - else
- %td{ colspan: "2" }
- %tr.footer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
- %div
- %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
- &middot;
- %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
- %div
- You're receiving this email because of your account on
- = succeed "." do
- %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
+ = @pipeline.short_sha
+ - if @merge_request
+ in
+ %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
+ = @merge_request.to_reference
+ .commit{ style: "color:#5c5c5c;font-weight:300;" }
+ = @pipeline.git_commit_message.truncate(50)
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ - commit = @pipeline.commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ - if commit.author
+ %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
+ = commit.author.name
+ - else
+ %span
+ = commit.author_name
+%tr.spacer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
+ &nbsp;
+- failed = @pipeline.statuses.latest.failed
+%tr.pre-section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;" }
+ Pipeline
+ %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
+ = "\##{@pipeline.id}"
+ had
+ = failed.size
+ failed
+ #{'build'.pluralize(failed.size)}.
+%tr.warning
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" }
+ Logs may contain sensitive data. Please consider before forwarding this email.
+%tr.section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" }
+ %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" }
+ %tbody
+ - failed.each do |build|
+ %tr.build-state
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;" }
+ %img{ alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" }
+ = build.stage
+ %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
+ = render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build
+ %tr.build-log
+ - if build.has_trace?
+ %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" }
+ %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" }
+ = build.trace_html(last_lines: 10).html_safe
+ - else
+ %td{ colspan: "2" }
diff --git a/app/views/notify/pipeline_failed_email.text.erb b/app/views/notify/pipeline_failed_email.text.erb
index ab91c7ef350..520a2fc7d68 100644
--- a/app/views/notify/pipeline_failed_email.text.erb
+++ b/app/views/notify/pipeline_failed_email.text.erb
@@ -27,7 +27,3 @@ Trace: <%= build.trace_with_state(last_lines: 10)[:text] %>
<% end -%>
<% end -%>
-
-You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>.
-Manage all notifications: <%= profile_notifications_url %>
-Help: <%= help_url %>
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index 8add2e18206..19d4add06f5 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -1,154 +1,84 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-%html{ lang: "en" }
- %head
- %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
- %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
- %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
- %title= message.subject
- :css
- /* CLIENT-SPECIFIC STYLES */
- body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
- table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
- img { -ms-interpolation-mode: bicubic; }
-
- /* iOS BLUE LINKS */
- a[x-apple-data-detectors] {
- color: inherit !important;
- text-decoration: none !important;
- font-size: inherit !important;
- font-family: inherit !important;
- font-weight: inherit !important;
- line-height: inherit !important;
- }
-
- /* ANDROID MARGIN HACK */
- body { margin:0 !important; }
- div[style*="margin: 16px 0"] { margin:0 !important; }
-
- @media only screen and (max-width: 639px) {
- body, #body {
- min-width: 320px !important;
- }
- table.wrapper {
- width: 100% !important;
- min-width: 320px !important;
- }
- table.wrapper > tbody > tr > td {
- border-left: 0 !important;
- border-right: 0 !important;
- border-radius: 0 !important;
- padding-left: 10px !important;
- padding-right: 10px !important;
- }
- }
- %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
+%tr.success
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
- %tr.line
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
- %tr.header
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
+ %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
+ Your pipeline has passed.
+%tr.spacer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
+ &nbsp;
+%tr.section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
+ - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
+ - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
+ %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
+ = namespace_name
+ \/
+ %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
+ = @project.name
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
+ = @pipeline.ref
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
+ = @pipeline.short_sha
+ - if @merge_request
+ in
+ %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
+ = @merge_request.to_reference
+ .commit{ style: "color:#5c5c5c;font-weight:300;" }
+ = @pipeline.git_commit_message.truncate(50)
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
- %tbody
- %tr.success
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
- %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- Your pipeline has passed.
- %tr.spacer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
- &nbsp;
- %tr.section
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
- %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
- = namespace_name
- \/
- %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
- = @project.name
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
- = @pipeline.ref
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
- = @pipeline.short_sha
- - if @merge_request
- in
- %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
- = @merge_request.to_reference
- .commit{ style: "color:#5c5c5c;font-weight:300;" }
- = @pipeline.git_commit_message.truncate(50)
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- - commit = @pipeline.commit
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- - if commit.author
- %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
- = commit.author.name
- - else
- %span
- = commit.author_name
- %tr.spacer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
- &nbsp;
- %tr.success-message
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" }
- - build_count = @pipeline.statuses.latest.size
- - stage_count = @pipeline.stages_count
- Pipeline
- %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
- = "\##{@pipeline.id}"
- successfully completed
- #{build_count} #{'build'.pluralize(build_count)}
- in
- #{stage_count} #{'stage'.pluralize(stage_count)}.
- %tr.footer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
- %div
- %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
- &middot;
- %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
- %div
- You're receiving this email because of your account on
- = succeed "." do
- %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
+ - commit = @pipeline.commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ - if commit.author
+ %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
+ = commit.author.name
+ - else
+ %span
+ = commit.author_name
+%tr.spacer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
+ &nbsp;
+%tr.success-message
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" }
+ - build_count = @pipeline.statuses.latest.size
+ - stage_count = @pipeline.stages_count
+ Pipeline
+ %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
+ = "\##{@pipeline.id}"
+ successfully completed
+ #{build_count} #{'build'.pluralize(build_count)}
+ in
+ #{stage_count} #{'stage'.pluralize(stage_count)}.
diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb
index 40e5e306426..0970a3a4e09 100644
--- a/app/views/notify/pipeline_success_email.text.erb
+++ b/app/views/notify/pipeline_success_email.text.erb
@@ -18,7 +18,3 @@ Commit Author: <%= commit.author_name %>
<% build_count = @pipeline.statuses.latest.size -%>
<% stage_count = @pipeline.stages_count -%>
Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) successfully completed <%= build_count %> <%= 'build'.pluralize(build_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>.
-
-You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>.
-Manage all notifications: <%= profile_notifications_url %>
-Help: <%= help_url %>
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index feadd863b00..df0a0212f3d 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -4,19 +4,6 @@
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
- Application theme
- %p
- This setting allows you to customize the appearance of the site, e.g. the sidebar.
- .col-lg-9.application-theme
- - Gitlab::Themes.each do |theme|
- = label_tag do
- .preview{ class: theme.css_class }
- = f.radio_button :theme_id, theme.id
- = theme.name
- .col-sm-12
- %hr
- .col-lg-3.profile-settings-sidebar
- %h4.prepend-top-0
Syntax highlighting theme
%p
This setting allow you to customize the appearance of the syntax.
diff --git a/app/views/profiles/preferences/update.js.erb b/app/views/profiles/preferences/update.js.erb
index 8966dd3fd86..431ab9d052b 100644
--- a/app/views/profiles/preferences/update.js.erb
+++ b/app/views/profiles/preferences/update.js.erb
@@ -1,7 +1,3 @@
-// Remove body class for any previous theme, re-add current one
-$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
-$('body').addClass('<%= user_application_theme %>')
-
// Toggle container-fluid class
if ('<%= current_user.layout %>' === 'fluid') {
$('.content-wrapper .container-fluid').removeClass('container-limited')
diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml
index 3b1a2e54ec2..d1f7f65bf53 100644
--- a/app/views/projects/blob/diff.html.haml
+++ b/app/views/projects/blob/diff.html.haml
@@ -25,6 +25,6 @@
= link_to raw(line_new), "##{line_new}"
= line_content
- - if @form.unfold? && @form.bottom? && @form.to < @blob.loc
+ - if @form.unfold? && @form.bottom? && @form.to < @blob.lines.size
%tr.line_holder{ id: @form.to, class: line_class }
= diff_match_line @form.to - @form.offset, @form.to, text: @match_line, view: diff_view, bottom: true
diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml
index 6abff6aaf95..c2b32a22170 100644
--- a/app/views/projects/commit/_pipeline.html.haml
+++ b/app/views/projects/commit/_pipeline.html.haml
@@ -3,7 +3,7 @@
.pull-right
- if can?(current_user, :update_pipeline, pipeline.project)
- if pipeline.builds.latest.failed.any?(&:retryable?)
- = link_to "Retry failed", retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-grouped btn-primary', method: :post
+ = link_to "Retry", retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'js-retry-button btn btn-grouped btn-primary', method: :post
- if pipeline.builds.running_or_pending.any?
= link_to "Cancel running", cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
index b87b79b170e..5c38b5ad9c0 100644
--- a/app/views/projects/diffs/_content.html.haml
+++ b/app/views/projects/diffs/_content.html.haml
@@ -15,10 +15,13 @@
%a.click-to-expand
Click to expand it.
- elsif diff_file.diff_lines.length > 0
+ - total_lines = 0
+ - if blob.lines.any?
+ - total_lines = blob.lines.last.chomp == '' ? blob.lines.size - 1 : blob.lines.size
- if diff_view == :parallel
- = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob
+ = render "projects/diffs/parallel_view", diff_file: diff_file, total_lines: total_lines
- else
- = render "projects/diffs/text_file", diff_file: diff_file
+ = render "projects/diffs/text_file", diff_file: diff_file, total_lines: total_lines
- else
- if diff_file.mode_changed?
.nothing-here-block File mode changed
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 074f1f634ae..997bf0fc560 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -43,7 +43,8 @@
- discussion_left, discussion_right = parallel_diff_discussions(left, right, diff_file)
- if discussion_left || discussion_right
= render "discussions/parallel_diff_discussion", discussion_left: discussion_left, discussion_right: discussion_right
- - if !diff_file.new_file && diff_file.diff_lines.any?
+ - if !diff_file.new_file && !diff_file.deleted_file && diff_file.diff_lines.any?
- last_line = diff_file.diff_lines.last
- %tr.line_holder.parallel
- = diff_match_line last_line.old_pos, last_line.new_pos, bottom: true, view: :parallel
+ - if last_line.new_pos < total_lines
+ %tr.line_holder.parallel
+ = diff_match_line last_line.old_pos, last_line.new_pos, bottom: true, view: :parallel
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index 2eea1db169a..ebd1a914ee7 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -10,7 +10,8 @@
as: :line,
locals: { diff_file: diff_file, discussions: discussions }
- - if !diff_file.new_file && diff_file.highlighted_diff_lines.any?
+ - if !diff_file.new_file && !diff_file.deleted_file && diff_file.highlighted_diff_lines.any?
- last_line = diff_file.highlighted_diff_lines.last
- %tr.line_holder
- = diff_match_line last_line.old_pos, last_line.new_pos, bottom: true
+ - if last_line.new_pos < total_lines
+ %tr.line_holder
+ = diff_match_line last_line.old_pos, last_line.new_pos, bottom: true
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index d3eb3b7055b..069f3d97943 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -40,7 +40,7 @@
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
- - if @issue.submittable_as_spam? && current_user.admin?
+ - if @issue.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
@@ -50,7 +50,7 @@
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- - if @issue.submittable_as_spam? && current_user.admin?
+ - if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
diff --git a/app/views/projects/issues/verify.html.haml b/app/views/projects/issues/verify.html.haml
index 1934b18c086..09aa401e44a 100644
--- a/app/views/projects/issues/verify.html.haml
+++ b/app/views/projects/issues/verify.html.haml
@@ -1,20 +1,4 @@
-- page_title "Anti-spam verification"
+- form = [@project.namespace.becomes(Namespace), @project, @issue]
-%h3.page-title
- Anti-spam verification
-%hr
-
-%p
- We detected potential spam in the issue description. Please verify that you are not a robot to submit the issue.
-
-= form_for [@project.namespace.becomes(Namespace), @project, @issue] do |f|
- .recaptcha
- - params[:issue].each do |field, value|
- = hidden_field(:issue, field, value: value)
- = hidden_field_tag(:merge_request_for_resolving_discussions, params[:merge_request_for_resolving_discussions])
- = hidden_field_tag(:spam_log_id, @issue.spam_log.id)
- = hidden_field_tag(:recaptcha_verification, true)
- = recaptcha_tags
-
- .row-content-block.footer-block
- = f.submit "Submit #{@issue.class.model_name.human.downcase}", class: 'btn btn-create'
+= render layout: 'layouts/recaptcha_verification', locals: { spammable: @issue, form: form } do
+ = hidden_field_tag(:merge_request_for_resolving_discussions, params[:merge_request_for_resolving_discussions])
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 144b3a9c8c8..83e6c026ba7 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -5,18 +5,19 @@
= render "projects/issues/head"
= render 'projects/last_push'
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('filtered_search')
+
%div{ class: container_class }
.top-area
= render 'shared/issuable/nav', type: :merge_requests
.nav-controls
- = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
-
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
- if merge_project
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
New Merge Request
- = render 'shared/issuable/filter', type: :merge_requests
+ = render 'shared/issuable/search_bar', type: :merge_requests
.merge-requests-holder
= render 'merge_requests'
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index a07885537b9..2a98bba05ee 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -22,7 +22,7 @@
- if current_user.can_select_namespace?
.input-group-addon
= root_url
- = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
+ = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
- else
.input-group-addon.static-namespace
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 1b08165c14c..a73e8f345e0 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -71,7 +71,7 @@
- if note_editable
.original-note-content.hidden{ data: { post_url: namespace_project_note_path(@project.namespace, @project, note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
#{note.note}
- %textarea.hidden.js-task-list-field.original-task-list= note.note
+ %textarea.hidden.js-task-list-field.original-task-list{ data: {update_url: namespace_project_note_path(@project.namespace, @project, note) } }= note.note
.note-awards
= render 'award_emoji/awards_block', awardable: note, inline: false
- if note.system
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index a6cd2d83bd5..0605af4fcd3 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -7,9 +7,9 @@
= commit_author_link(@commit)
.header-action-buttons
- if can?(current_user, :update_pipeline, @pipeline.project)
- - if @pipeline.builds.latest.failed.any?(&:retryable?)
- = link_to "Retry failed", retry_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'btn btn-inverted-secondary', method: :post
- - if @pipeline.builds.running_or_pending.any?
+ - if @pipeline.retryable?
+ = link_to "Retry", retry_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'js-retry-button btn btn-inverted-secondary', method: :post
+ - if @pipeline.cancelable?
= link_to "Cancel running", cancel_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
- if @commit
diff --git a/app/views/projects/pipelines_settings/_badge.html.haml b/app/views/projects/pipelines_settings/_badge.html.haml
index 22a3b884520..43bbd735059 100644
--- a/app/views/projects/pipelines_settings/_badge.html.haml
+++ b/app/views/projects/pipelines_settings/_badge.html.haml
@@ -25,3 +25,10 @@
HTML
.col-md-10.code.js-syntax-highlight
= highlight('.html', badge.to_html)
+ .row
+ %hr
+ .row
+ .col-md-2.text-center
+ AsciiDoc
+ .col-md-10.code.js-syntax-highlight
+ = highlight('.adoc', badge.to_asciidoc)
diff --git a/app/views/projects/pipelines_settings/_show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml
index 8024fb8979d..132f6372e40 100644
--- a/app/views/projects/pipelines_settings/_show.html.haml
+++ b/app/views/projects/pipelines_settings/_show.html.haml
@@ -60,7 +60,7 @@
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group
%span.input-group-addon /
- = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
+ = f.text_field :build_coverage_regex, class: 'form-control', placeholder: 'Regular expression'
%span.input-group-addon /
%p.help-block
A regular expression that will be used to find the test coverage
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
index dde2e2b644d..34ee4ff1937 100644
--- a/app/views/projects/snippets/_actions.html.haml
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -10,7 +10,7 @@
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do
New snippet
- - if @snippet.submittable_as_spam? && current_user.admin?
+ - if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
.visible-xs-block.dropdown
@@ -31,6 +31,6 @@
%li
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
Edit
- - if @snippet.submittable_as_spam? && current_user.admin?
+ - if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post
diff --git a/app/views/projects/snippets/verify.html.haml b/app/views/projects/snippets/verify.html.haml
new file mode 100644
index 00000000000..eb56f03b3f4
--- /dev/null
+++ b/app/views/projects/snippets/verify.html.haml
@@ -0,0 +1,4 @@
+- form = [@project.namespace.becomes(Namespace), @project, @snippet.becomes(Snippet)]
+
+= render 'layouts/recaptcha_verification', spammable: @snippet, form: form
+
diff --git a/app/views/projects/variables/_form.html.haml b/app/views/projects/variables/_form.html.haml
index a5bae83e0ce..1ae86d258af 100644
--- a/app/views/projects/variables/_form.html.haml
+++ b/app/views/projects/variables/_form.html.haml
@@ -6,5 +6,5 @@
= f.text_field :key, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true
.form-group
= f.label :value, "Value", class: "label-light"
- = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true
+ = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE"
= f.submit btn_text, class: "btn btn-save"
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 7fe2bce3e7c..22004ecacbc 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -11,7 +11,7 @@
.results.prepend-top-10
- if @scope == 'commits'
- %ul.list-unstyled
+ %ul.content-list.commit-list.table-list.table-wide
= render partial: "search/results/commit", collection: @search_objects
- else
.search-results
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index e977c1f1698..f84be600df8 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -12,41 +12,34 @@
%span.light= time_ago_with_tooltip(snippet.created_at)
%h4.snippet-title
- snippet_path = reliable_snippet_path(snippet)
- = link_to snippet_path do
- .file-holder
- .js-file-title.file-title
+ .file-holder
+ .js-file-title.file-title
+ = link_to snippet_path do
%i.fa.fa-file
%strong= snippet.file_name
- - if markup?(snippet.file_name)
- .file-content.wiki
+ - if markup?(snippet.file_name)
+ .file-content.wiki
+ - snippet_chunks.each do |chunk|
+ - unless chunk[:data].empty?
+ = render_markup(snippet.file_name, chunk[:data])
+ - else
+ .file-content.code
+ .nothing-here-block Empty file
+ - else
+ .file-content.code.js-syntax-highlight
+ .line-numbers
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
- = render_markup(snippet.file_name, chunk[:data])
+ - Gitlab::Git::Util.count_lines(chunk[:data]).times do |index|
+ - offset = defined?(chunk[:start_line]) ? chunk[:start_line] : 1
+ - i = index + offset
+ = link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}", class: "diff-line-num" do
+ %i.fa.fa-link
+ = i
+ .blob-content
+ - snippet_chunks.each do |chunk|
+ - unless chunk[:data].empty?
+ = highlight(snippet.file_name, chunk[:data], repository: nil, plain: snippet.no_highlighting?)
- else
.file-content.code
.nothing-here-block Empty file
- - else
- .file-content.code.js-syntax-highlight
- .line-numbers
- - snippet_chunks.each do |chunk|
- - unless chunk[:data].empty?
- - Gitlab::Git::Util.count_lines(chunk[:data]).times do |index|
- - offset = defined?(chunk[:start_line]) ? chunk[:start_line] : 1
- - i = index + offset
- = link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}", class: "diff-line-num" do
- %i.fa.fa-link
- = i
- - unless snippet == snippet_chunks.last
- %a.diff-line-num
- = "."
- %pre.code
- %code
- - snippet_chunks.each do |chunk|
- - unless chunk[:data].empty?
- = chunk[:data]
- - unless chunk == snippet_chunks.last
- %a
- = "..."
- - else
- .file-content.code
- .nothing-here-block Empty file
diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml
index 1264e524d86..66310da5cd6 100644
--- a/app/views/shared/_issuable_meta_data.html.haml
+++ b/app/views/shared/_issuable_meta_data.html.haml
@@ -2,6 +2,12 @@
- issue_votes = @issuable_meta_data[issuable.id]
- upvotes, downvotes = issue_votes.upvotes, issue_votes.downvotes
- issuable_url = @collection_type == "Issue" ? issue_path(issuable, anchor: 'notes') : merge_request_path(issuable, anchor: 'notes')
+- issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count
+
+- if issuable_mr > 0
+ %li
+ = image_tag('icon-merge-request-unmerged', class: 'icon-merge-request-unmerged')
+ = issuable_mr
- if upvotes > 0
%li
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index ead9b84b991..1744a597c51 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -1,6 +1,4 @@
- label_css_id = dom_id(label)
-- open_issues_count = label.open_issues_count(current_user)
-- open_merge_requests_count = label.open_merge_requests_count(current_user)
- status = label_subscription_status(label, @project).inquiry if current_user
- subject = local_assigns[:subject]
@@ -15,10 +13,10 @@
%ul
%li
= link_to_label(label, subject: subject, type: :merge_request) do
- = pluralize open_merge_requests_count, 'merge request'
+ view merge requests
%li
= link_to_label(label, subject: subject) do
- = pluralize open_issues_count, 'open issue'
+ view open issues
- if current_user && defined?(@project)
%li.label-subscription
- if label.is_a?(ProjectLabel)
@@ -40,9 +38,9 @@
.pull-right.hidden-xs.hidden-sm.hidden-md
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
- = pluralize open_merge_requests_count, 'merge request'
+ view merge requests
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
- = pluralize open_issues_count, 'open issue'
+ view open issues
- if current_user && defined?(@project)
.label-subscription.inline
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 6e417aa2251..8e04b50bb8a 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -32,7 +32,7 @@
{{hint}}
%span.js-filter-tag.dropdown-light-content
{{tag}}
- #js-dropdown-author.dropdown-menu
+ #js-dropdown-author.dropdown-menu{ data: { icon: 'pencil', hint: 'author', tag: '@author' } }
%ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
%li.filter-dropdown-item
%button.btn.btn-link.dropdown-user
@@ -42,7 +42,7 @@
{{name}}
%span.dropdown-light-content
@{{username}}
- #js-dropdown-assignee.dropdown-menu
+ #js-dropdown-assignee.dropdown-menu{ data: { icon: 'user', hint: 'assignee', tag: '@assignee' } }
%ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link
@@ -57,7 +57,7 @@
{{name}}
%span.dropdown-light-content
@{{username}}
- #js-dropdown-milestone.dropdown-menu{ 'data-dropdown' => true }
+ #js-dropdown-milestone.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } }
%ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link
@@ -70,7 +70,7 @@
%li.filter-dropdown-item
%button.btn.btn-link.js-data-value
{{title}}
- #js-dropdown-label.dropdown-menu{ 'data-dropdown' => true }
+ #js-dropdown-label.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label' } }
%ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 239387fc9fa..8e721c9c8dd 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -61,7 +61,7 @@
= dropdown_title("Change permissions")
.dropdown-content
%ul
- - Gitlab::Access.options.each do |role, role_id|
+ - member.class.access_level_roles.each do |role, role_id|
%li
= link_to role, "javascript:void(0)",
class: ("is-active" if member.access_level == role_id),
diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml
index 31eb07ca666..a93cbd1041f 100644
--- a/app/views/shared/milestones/_issuables.html.haml
+++ b/app/views/shared/milestones/_issuables.html.haml
@@ -3,11 +3,11 @@
- panel_class = primary ? 'panel-primary' : 'panel-default'
.panel{ class: panel_class }
- .panel-heading.split
- .left
+ .panel-heading
+ .title
= title
- if show_counter
- .right
+ .counter
= number_with_delimiter(issuables.size)
- class_prefix = dom_class(issuables).pluralize
diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml
index 855a995afa9..a7f118d3f7d 100644
--- a/app/views/snippets/_actions.html.haml
+++ b/app/views/snippets/_actions.html.haml
@@ -9,7 +9,7 @@
Delete
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do
New snippet
- - if @snippet.submittable_as_spam? && current_user.admin?
+ - if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
.visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
@@ -28,6 +28,6 @@
%li
= link_to edit_snippet_path(@snippet) do
Edit
- - if @snippet.submittable_as_spam? && current_user.admin?
+ - if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post
diff --git a/app/views/snippets/verify.html.haml b/app/views/snippets/verify.html.haml
new file mode 100644
index 00000000000..cb623ccab57
--- /dev/null
+++ b/app/views/snippets/verify.html.haml
@@ -0,0 +1,4 @@
+- form = [@snippet.becomes(Snippet)]
+
+= render 'layouts/recaptcha_verification', spammable: @snippet, form: form
+
diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb
index 5483bbb210b..3340a7be4fe 100644
--- a/app/workers/delete_user_worker.rb
+++ b/app/workers/delete_user_worker.rb
@@ -7,5 +7,7 @@ class DeleteUserWorker
current_user = User.find(current_user_id)
Users::DestroyService.new(current_user).execute(delete_user, options.symbolize_keys)
+ rescue Gitlab::Access::AccessDeniedError => e
+ Rails.logger.warn("User could not be destroyed: #{e}")
end
end