summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/abuse_reports.js.es61
-rw-r--r--app/assets/javascripts/activities.js5
-rw-r--r--app/assets/javascripts/admin.js1
-rw-r--r--app/assets/javascripts/api.js1
-rw-r--r--app/assets/javascripts/application.js16
-rw-r--r--app/assets/javascripts/aside.js1
-rw-r--r--app/assets/javascripts/autosave.js1
-rw-r--r--app/assets/javascripts/awards_handler.js12
-rw-r--r--app/assets/javascripts/behaviors/autosize.js1
-rw-r--r--app/assets/javascripts/behaviors/details_behavior.js6
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js1
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js1
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js1
-rw-r--r--app/assets/javascripts/blob/blob_ci_yaml.js.es61
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js1
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selector.js1
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selectors.js1
-rw-r--r--app/assets/javascripts/blob/blob_license_selector.js1
-rw-r--r--app/assets/javascripts/blob/blob_license_selectors.js.es61
-rw-r--r--app/assets/javascripts/blob/template_selector.js.es68
-rw-r--r--app/assets/javascripts/blob_edit/blob_edit_bundle.js1
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js1
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js.es614
-rw-r--r--app/assets/javascripts/boards/components/board.js.es622
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.js.es61
-rw-r--r--app/assets/javascripts/boards/components/board_card.js.es635
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js.es61
-rw-r--r--app/assets/javascripts/boards/components/board_list.js.es61
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js.es68
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js.es653
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js.es62
-rw-r--r--app/assets/javascripts/boards/filters/due_date_filters.js.es65
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js.es65
-rw-r--r--app/assets/javascripts/boards/models/issue.js.es624
-rw-r--r--app/assets/javascripts/boards/models/label.js.es61
-rw-r--r--app/assets/javascripts/boards/models/list.js.es61
-rw-r--r--app/assets/javascripts/boards/models/milestone.js.es67
-rw-r--r--app/assets/javascripts/boards/models/user.js.es61
-rw-r--r--app/assets/javascripts/boards/services/board_service.js.es63
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js.es611
-rw-r--r--app/assets/javascripts/boards/test_utils/simulate_drag.js1
-rw-r--r--app/assets/javascripts/boards/vue_resource_interceptor.js.es61
-rw-r--r--app/assets/javascripts/breakpoints.js1
-rw-r--r--app/assets/javascripts/broadcast_message.js1
-rw-r--r--app/assets/javascripts/build.js1
-rw-r--r--app/assets/javascripts/build_artifacts.js1
-rw-r--r--app/assets/javascripts/build_variables.js.es61
-rw-r--r--app/assets/javascripts/commit.js1
-rw-r--r--app/assets/javascripts/commit/file.js1
-rw-r--r--app/assets/javascripts/commit/image_file.js1
-rw-r--r--app/assets/javascripts/commits.js1
-rw-r--r--app/assets/javascripts/compare.js1
-rw-r--r--app/assets/javascripts/compare_autocomplete.js.es61
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js1
-rw-r--r--app/assets/javascripts/copy_to_clipboard.js1
-rw-r--r--app/assets/javascripts/create_label.js.es61
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 (renamed from app/assets/javascripts/cycle_analytics.js.es6)7
-rw-r--r--app/assets/javascripts/diff.js1
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/mixins/discussion.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/models/note.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/stores/comments.js.es61
-rw-r--r--app/assets/javascripts/dispatcher.js.es61
-rw-r--r--app/assets/javascripts/dropzone_input.js1
-rw-r--r--app/assets/javascripts/due_date_select.js.es630
-rw-r--r--app/assets/javascripts/extensions/array.js1
-rw-r--r--app/assets/javascripts/extensions/element.js.es67
-rw-r--r--app/assets/javascripts/extensions/jquery.js1
-rw-r--r--app/assets/javascripts/files_comment_button.js1
-rw-r--r--app/assets/javascripts/flash.js1
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es61
-rw-r--r--app/assets/javascripts/gl_dropdown.js41
-rw-r--r--app/assets/javascripts/gl_field_errors.js.es61
-rw-r--r--app/assets/javascripts/gl_form.js1
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js3
-rw-r--r--app/assets/javascripts/graphs/stat_graph.js1
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors.js1
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js12
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_util.js1
-rw-r--r--app/assets/javascripts/group_avatar.js1
-rw-r--r--app/assets/javascripts/groups_select.js1
-rw-r--r--app/assets/javascripts/header.js10
-rw-r--r--app/assets/javascripts/importer_status.js1
-rw-r--r--app/assets/javascripts/issuable.js.es657
-rw-r--r--app/assets/javascripts/issuable_context.js1
-rw-r--r--app/assets/javascripts/issuable_form.js5
-rw-r--r--app/assets/javascripts/issue.js7
-rw-r--r--app/assets/javascripts/issue_status_select.js1
-rw-r--r--app/assets/javascripts/issues_bulk_assignment.js.es61
-rw-r--r--app/assets/javascripts/label_manager.js.es61
-rw-r--r--app/assets/javascripts/labels.js1
-rw-r--r--app/assets/javascripts/labels_select.js30
-rw-r--r--app/assets/javascripts/layout_nav.js1
-rw-r--r--app/assets/javascripts/lib/ace.js1
-rw-r--r--app/assets/javascripts/lib/chart.js1
-rw-r--r--app/assets/javascripts/lib/cropper.js1
-rw-r--r--app/assets/javascripts/lib/d3.js1
-rw-r--r--app/assets/javascripts/lib/raphael.js1
-rw-r--r--app/assets/javascripts/lib/utils/animate.js1
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js9
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js1
-rw-r--r--app/assets/javascripts/lib/utils/jquery.timeago.js1
-rw-r--r--app/assets/javascripts/lib/utils/notify.js1
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js4
-rw-r--r--app/assets/javascripts/lib/utils/type_utility.js1
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js1
-rw-r--r--app/assets/javascripts/line_highlighter.js1
-rw-r--r--app/assets/javascripts/logo.js1
-rw-r--r--app/assets/javascripts/member_expiration_date.js1
-rw-r--r--app/assets/javascripts/members.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es67
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es61
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es61
-rw-r--r--app/assets/javascripts/merge_request.js7
-rw-r--r--app/assets/javascripts/merge_request_tabs.js23
-rw-r--r--app/assets/javascripts/merge_request_widget.js.es61
-rw-r--r--app/assets/javascripts/merged_buttons.js1
-rw-r--r--app/assets/javascripts/milestone.js1
-rw-r--r--app/assets/javascripts/milestone_select.js22
-rw-r--r--app/assets/javascripts/namespace_select.js1
-rw-r--r--app/assets/javascripts/network/branch_graph.js1
-rw-r--r--app/assets/javascripts/network/network.js1
-rw-r--r--app/assets/javascripts/network/network_bundle.js3
-rw-r--r--app/assets/javascripts/new_branch_form.js1
-rw-r--r--app/assets/javascripts/new_commit_form.js1
-rw-r--r--app/assets/javascripts/notes.js1
-rw-r--r--app/assets/javascripts/notifications_dropdown.js1
-rw-r--r--app/assets/javascripts/notifications_form.js1
-rw-r--r--app/assets/javascripts/pager.js1
-rw-r--r--app/assets/javascripts/pipelines.js.es648
-rw-r--r--app/assets/javascripts/preview_markdown.js1
-rw-r--r--app/assets/javascripts/profile/gl_crop.js.es61
-rw-r--r--app/assets/javascripts/profile/profile.js.es61
-rw-r--r--app/assets/javascripts/profile/profile_bundle.js1
-rw-r--r--app/assets/javascripts/project.js11
-rw-r--r--app/assets/javascripts/project_avatar.js1
-rw-r--r--app/assets/javascripts/project_find_file.js1
-rw-r--r--app/assets/javascripts/project_fork.js1
-rw-r--r--app/assets/javascripts/project_import.js1
-rw-r--r--app/assets/javascripts/project_new.js1
-rw-r--r--app/assets/javascripts/project_select.js1
-rw-r--r--app/assets/javascripts/project_show.js1
-rw-r--r--app/assets/javascripts/projects_list.js1
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es61
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js.es61
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es61
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js.es61
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es61
-rw-r--r--app/assets/javascripts/protected_branches/protected_branches_bundle.js1
-rw-r--r--app/assets/javascripts/right_sidebar.js27
-rw-r--r--app/assets/javascripts/search.js1
-rw-r--r--app/assets/javascripts/search_autocomplete.js.es61
-rw-r--r--app/assets/javascripts/shortcuts.js1
-rw-r--r--app/assets/javascripts/shortcuts_blob.js1
-rw-r--r--app/assets/javascripts/shortcuts_dashboard_navigation.js1
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js1
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js1
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js1
-rw-r--r--app/assets/javascripts/shortcuts_network.js1
-rw-r--r--app/assets/javascripts/sidebar.js.es615
-rw-r--r--app/assets/javascripts/single_file_diff.js20
-rw-r--r--app/assets/javascripts/snippet/snippet_bundle.js1
-rw-r--r--app/assets/javascripts/snippets_list.js.es61
-rw-r--r--app/assets/javascripts/star.js3
-rw-r--r--app/assets/javascripts/subscription.js33
-rw-r--r--app/assets/javascripts/subscription_select.js1
-rw-r--r--app/assets/javascripts/syntax_highlight.js1
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js.es613
-rw-r--r--app/assets/javascripts/templates/issuable_template_selectors.js.es61
-rw-r--r--app/assets/javascripts/todos.js.es64
-rw-r--r--app/assets/javascripts/tree.js1
-rw-r--r--app/assets/javascripts/u2f/authenticate.js1
-rw-r--r--app/assets/javascripts/u2f/error.js1
-rw-r--r--app/assets/javascripts/u2f/register.js1
-rw-r--r--app/assets/javascripts/u2f/util.js1
-rw-r--r--app/assets/javascripts/user.js.es66
-rw-r--r--app/assets/javascripts/user_tabs.js.es61
-rw-r--r--app/assets/javascripts/username_validator.js.es61
-rw-r--r--app/assets/javascripts/users/calendar.js1
-rw-r--r--app/assets/javascripts/users/users_bundle.js1
-rw-r--r--app/assets/javascripts/users_select.js46
-rw-r--r--app/assets/javascripts/wikis.js1
-rw-r--r--app/assets/javascripts/zen_mode.js1
-rw-r--r--app/assets/stylesheets/framework/avatar.scss63
-rw-r--r--app/assets/stylesheets/framework/buttons.scss2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss9
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss87
-rw-r--r--app/assets/stylesheets/framework/header.scss8
-rw-r--r--app/assets/stylesheets/framework/lists.scss8
-rw-r--r--app/assets/stylesheets/framework/pagination.scss62
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss17
-rw-r--r--app/assets/stylesheets/framework/timeline.scss2
-rw-r--r--app/assets/stylesheets/framework/typography.scss10
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/pages/admin.scss9
-rw-r--r--app/assets/stylesheets/pages/awards.scss8
-rw-r--r--app/assets/stylesheets/pages/boards.scss66
-rw-r--r--app/assets/stylesheets/pages/builds.scss26
-rw-r--r--app/assets/stylesheets/pages/commit.scss62
-rw-r--r--app/assets/stylesheets/pages/commits.scss31
-rw-r--r--app/assets/stylesheets/pages/dashboard.scss4
-rw-r--r--app/assets/stylesheets/pages/diff.scss6
-rw-r--r--app/assets/stylesheets/pages/groups.scss9
-rw-r--r--app/assets/stylesheets/pages/login.scss1
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss10
-rw-r--r--app/assets/stylesheets/pages/notes.scss5
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss11
-rw-r--r--app/assets/stylesheets/pages/profile.scss4
-rw-r--r--app/assets/stylesheets/pages/projects.scss12
-rw-r--r--app/assets/stylesheets/pages/search.scss2
-rw-r--r--app/assets/stylesheets/pages/todos.scss60
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss2
-rw-r--r--app/assets/stylesheets/print.scss2
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/concerns/service_params.rb2
-rw-r--r--app/controllers/projects/boards/issues_controller.rb9
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/labels_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects/project_members_controller.rb17
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb5
-rw-r--r--app/finders/issuable_finder.rb4
-rw-r--r--app/finders/labels_finder.rb17
-rw-r--r--app/helpers/button_helper.rb3
-rw-r--r--app/helpers/events_helper.rb6
-rw-r--r--app/helpers/issuables_helper.rb8
-rw-r--r--app/helpers/projects_helper.rb6
-rw-r--r--app/helpers/sidekiq_helper.rb2
-rw-r--r--app/models/ci/pipeline.rb12
-rw-r--r--app/models/commit_status.rb12
-rw-r--r--app/models/concerns/issuable.rb1
-rw-r--r--app/models/concerns/project_features_compatibility.rb2
-rw-r--r--app/models/concerns/sortable.rb11
-rw-r--r--app/models/concerns/taskable.rb16
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/group_label.rb4
-rw-r--r--app/models/issue.rb5
-rw-r--r--app/models/label.rb30
-rw-r--r--app/models/lfs_object.rb6
-rw-r--r--app/models/merge_request.rb1
-rw-r--r--app/models/project.rb9
-rw-r--r--app/models/project_label.rb4
-rw-r--r--app/models/project_services/bugzilla_service.rb2
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb18
-rw-r--r--app/models/project_services/jira_service.rb232
-rw-r--r--app/models/project_services/redmine_service.rb2
-rw-r--r--app/models/project_team.rb10
-rw-r--r--app/models/repository.rb38
-rw-r--r--app/models/todo.rb2
-rw-r--r--app/models/user.rb22
-rw-r--r--app/services/ci/process_pipeline_service.rb23
-rw-r--r--app/services/ci/register_build_service.rb11
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/delete_tag_service.rb2
-rw-r--r--app/services/git_tag_push_service.rb4
-rw-r--r--app/services/labels/find_or_create_service.rb11
-rw-r--r--app/services/members/approve_access_request_service.rb21
-rw-r--r--app/services/members/create_service.rb16
-rw-r--r--app/services/merge_requests/build_service.rb50
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/views/admin/groups/_group.html.haml3
-rw-r--r--app/views/admin/groups/show.html.haml3
-rw-r--r--app/views/admin/projects/index.html.haml3
-rw-r--r--app/views/admin/users/index.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml147
-rw-r--r--app/views/groups/edit.html.haml3
-rw-r--r--app/views/groups/labels/index.html.haml2
-rw-r--r--app/views/groups/show.html.haml3
-rw-r--r--app/views/help/ui.html.haml2
-rw-r--r--app/views/kaminari/gitlab/_gap.html.haml4
-rw-r--r--app/views/kaminari/gitlab/_page.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml6
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml85
-rw-r--r--app/views/projects/_home_panel.html.haml3
-rw-r--r--app/views/projects/boards/components/_board.html.haml4
-rw-r--r--app/views/projects/boards/components/_card.html.haml17
-rw-r--r--app/views/projects/boards/components/_sidebar.html.haml23
-rw-r--r--app/views/projects/boards/components/sidebar/_assignee.html.haml40
-rw-r--r--app/views/projects/boards/components/sidebar/_due_date.html.haml32
-rw-r--r--app/views/projects/boards/components/sidebar/_labels.html.haml30
-rw-r--r--app/views/projects/boards/components/sidebar/_milestone.html.haml28
-rw-r--r--app/views/projects/boards/components/sidebar/_notifications.html.haml11
-rw-r--r--app/views/projects/boards/index.html.haml10
-rw-r--r--app/views/projects/boards/show.html.haml10
-rw-r--r--app/views/projects/branches/_branch.html.haml9
-rw-r--r--app/views/projects/builds/_header.html.haml29
-rw-r--r--app/views/projects/builds/_sidebar.html.haml2
-rw-r--r--app/views/projects/buttons/_fork.html.haml4
-rw-r--r--app/views/projects/buttons/_star.html.haml2
-rw-r--r--app/views/projects/ci/builds/_build_pipeline.html.haml7
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml7
-rw-r--r--app/views/projects/commit/_commit_box.html.haml74
-rw-r--r--app/views/projects/commit/_pipeline_status_group.html.haml2
-rw-r--r--app/views/projects/commit/_pipelines_list.haml2
-rw-r--r--app/views/projects/commit/builds.html.haml3
-rw-r--r--app/views/projects/commit/pipelines.html.haml3
-rw-r--r--app/views/projects/commit/show.html.haml3
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml4
-rw-r--r--app/views/projects/diffs/_file.html.haml1
-rw-r--r--app/views/projects/diffs/_file_header.html.haml2
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml2
-rw-r--r--app/views/projects/edit.html.haml3
-rw-r--r--app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml4
-rw-r--r--app/views/projects/issues/_related_branches.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml4
-rw-r--r--app/views/projects/pipelines_settings/show.html.haml57
-rw-r--r--app/views/projects/tags/_tag.html.haml2
-rw-r--r--app/views/shared/_event_filter.html.haml9
-rw-r--r--app/views/shared/_label.html.haml13
-rw-r--r--app/views/shared/empty_states/_todos_all_done.svg1
-rw-r--r--app/views/shared/empty_states/_todos_empty.svg110
-rw-r--r--app/views/shared/groups/_group.html.haml3
-rw-r--r--app/views/shared/icons/_icon_close.svg1
-rw-r--r--app/views/shared/milestones/_issuable.html.haml3
-rw-r--r--app/views/shared/projects/_project.html.haml9
-rw-r--r--app/views/users/_groups.html.haml3
-rw-r--r--app/workers/project_cache_worker.rb16
-rw-r--r--app/workers/project_web_hook_worker.rb2
-rw-r--r--app/workers/remove_unreferenced_lfs_objects_worker.rb8
336 files changed, 2220 insertions, 833 deletions
diff --git a/app/assets/javascripts/abuse_reports.js.es6 b/app/assets/javascripts/abuse_reports.js.es6
index 2fe46b9fd06..82e526ae0ef 100644
--- a/app/assets/javascripts/abuse_reports.js.es6
+++ b/app/assets/javascripts/abuse_reports.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
const MAX_MESSAGE_LENGTH = 500;
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js
index f4f8cf04184..59ac9b9cef5 100644
--- a/app/assets/javascripts/activities.js
+++ b/app/assets/javascripts/activities.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Activities = (function() {
function Activities() {
@@ -24,9 +25,7 @@
var filter = sender.attr("id").split("_")[0];
$('.event-filter .active').removeClass("active");
- $.cookie("event_filter", filter, {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set("event_filter", filter);
sender.closest('li').toggleClass("active");
};
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index f8460beb5d2..1ef340e4ca1 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Admin = (function() {
function Admin() {
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 56ec1489f89..7ebe1599fca 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Api = {
groupsPath: "/api/:version/groups.json",
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 17cbfd0e66f..7dd9adac736 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -1,5 +1,6 @@
+/* eslint-disable */
// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
@@ -11,13 +12,13 @@
/*= require jquery-ui/effect-highlight */
/*= require jquery-ui/sortable */
/*= require jquery_ujs */
-/*= require jquery.cookie */
/*= require jquery.endless-scroll */
/*= require jquery.highlight */
/*= require jquery.waitforimages */
/*= require jquery.atwho */
/*= require jquery.scrollTo */
/*= require jquery.turbolinks */
+/*= require js.cookie */
/*= require turbolinks */
/*= require autosave */
/*= require bootstrap/affix */
@@ -124,15 +125,11 @@
return str.replace(/<(?:.|\n)*?>/gm, '');
};
- window.unbindEvents = function() {
- return $(document).off('scroll');
- };
-
window.shiftWindow = function() {
return scrollBy(0, -100);
};
- document.addEventListener("page:fetch", unbindEvents);
+ document.addEventListener("page:fetch", gl.utils.cleanupBeforeFetch);
window.addEventListener("hashchange", shiftWindow);
@@ -149,6 +146,10 @@
$document = $(document);
$window = $(window);
$body = $('body');
+
+ // Set the default path for all cookies to GitLab's root directory
+ Cookies.defaults.path = gon.relative_url_root || '/';
+
gl.utils.preventDisabledButtons();
bootstrapBreakpoint = bp.getBreakpointSize();
$(".nav-sidebar").niceScroll({
@@ -187,6 +188,7 @@
// Close select2 on escape
});
// Initialize tooltips
+ $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover';
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
placement: function(_, el) {
diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js
index 7b546e79ee0..c7eff27f971 100644
--- a/app/assets/javascripts/aside.js
+++ b/app/assets/javascripts/aside.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Aside = (function() {
function Aside() {
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index a9aec6e8ea4..ab09e4475e6 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Autosave = (function() {
function Autosave(field, key) {
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 44af1c135a0..8bdb0965f99 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.AwardsHandler = (function() {
const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
@@ -91,7 +92,7 @@
css = {
top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px"
};
- if ((position != null) && position === 'right') {
+ if (position === 'right') {
css.left = (($addBtn.offset().left - $menu.outerWidth()) + 20) + "px";
$menu.addClass('is-aligned-right');
} else {
@@ -322,21 +323,18 @@
var frequentlyUsedEmojis;
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
frequentlyUsedEmojis.push(emoji);
- return $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), {
- path: gon.relative_url_root || '/',
- expires: 365
- });
+ Cookies.set('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 });
};
AwardsHandler.prototype.getFrequentlyUsedEmojis = function() {
var frequentlyUsedEmojis;
- frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',');
+ frequentlyUsedEmojis = (Cookies.get('frequently_used_emojis') || '').split(',');
return _.compact(_.uniq(frequentlyUsedEmojis));
};
AwardsHandler.prototype.renderFrequentlyUsedBlock = function() {
var emoji, frequentlyUsedEmojis, i, len, ul;
- if ($.cookie('frequently_used_emojis')) {
+ if (Cookies.get('frequently_used_emojis')) {
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>");
for (i = 0, len = frequentlyUsedEmojis.length; i < len; i++) {
diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js
index dc8ae601961..074378b9e52 100644
--- a/app/assets/javascripts/behaviors/autosize.js
+++ b/app/assets/javascripts/behaviors/autosize.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require jquery.ba-resize */
/*= require autosize */
diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js
index 1df681a4816..a64cefb62bd 100644
--- a/app/assets/javascripts/behaviors/details_behavior.js
+++ b/app/assets/javascripts/behaviors/details_behavior.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
$(function() {
$("body").on("click", ".js-details-target", function() {
@@ -14,6 +15,11 @@
return $("body").on("click", ".js-details-expand", function(e) {
$(this).next('.js-details-content').removeClass("hide");
$(this).hide();
+
+ var truncatedItem = $(this).siblings('.js-details-short');
+ if (truncatedItem.length) {
+ truncatedItem.addClass("hide");
+ }
return e.preventDefault();
});
});
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index 54b7360ab41..7ff88ecdcaf 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Quick Submit behavior
//
// When a child field of a form with a `js-quick-submit` class receives a
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index 894034bdd54..4ac343f876c 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Requires Input behavior
//
// When called on a form with input fields with the `required` attribute, the
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index a6ce378d67a..05b213fe3fb 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function(w) {
$(function() {
// Toggle button. Show/hide content inside parent container.
diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js.es6 b/app/assets/javascripts/blob/blob_ci_yaml.js.es6
index d6ea4f84f57..37531aaec9b 100644
--- a/app/assets/javascripts/blob/blob_ci_yaml.js.es6
+++ b/app/assets/javascripts/blob/blob_ci_yaml.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require blob/template_selector */
((global) => {
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index 8cca1aa9232..33fb4f8185c 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.BlobFileDropzone = (function() {
function BlobFileDropzone(form, method) {
diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js b/app/assets/javascripts/blob/blob_gitignore_selector.js
index cd746b05cf6..344fe5dcd94 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selector.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selector.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require blob/template_selector */
diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js
index 4e9500428b2..9e992f7913c 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selectors.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selectors.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.BlobGitignoreSelectors = (function() {
function BlobGitignoreSelectors(opts) {
diff --git a/app/assets/javascripts/blob/blob_license_selector.js b/app/assets/javascripts/blob/blob_license_selector.js
index 2701df3e6de..41a83a56146 100644
--- a/app/assets/javascripts/blob/blob_license_selector.js
+++ b/app/assets/javascripts/blob/blob_license_selector.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require blob/template_selector */
diff --git a/app/assets/javascripts/blob/blob_license_selectors.js.es6 b/app/assets/javascripts/blob/blob_license_selectors.js.es6
index 153ed457559..adeb8ba1318 100644
--- a/app/assets/javascripts/blob/blob_license_selectors.js.es6
+++ b/app/assets/javascripts/blob/blob_license_selectors.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class BlobLicenseSelectors {
constructor({ $dropdowns, editor }) {
diff --git a/app/assets/javascripts/blob/template_selector.js.es6 b/app/assets/javascripts/blob/template_selector.js.es6
index 4e309e480b0..5434a19bcec 100644
--- a/app/assets/javascripts/blob/template_selector.js.es6
+++ b/app/assets/javascripts/blob/template_selector.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class TemplateSelector {
constructor({ dropdown, data, pattern, wrapper, editor, fileEndpoint, $input } = {}) {
@@ -68,14 +69,10 @@
// To be implemented on the extending class
// e.g.
// Api.gitignoreText item.name, @requestFileSuccess.bind(@)
- requestFileSuccess(file, { skipFocus, append } = {}) {
+ requestFileSuccess(file, { skipFocus } = {}) {
const oldValue = this.editor.getValue();
let newValue = file.content;
- if (append && oldValue.length && oldValue !== newValue) {
- newValue = oldValue + '\n\n' + newValue;
- }
-
this.editor.setValue(newValue, 1);
if (!skipFocus) this.editor.focus();
@@ -99,4 +96,3 @@
global.TemplateSelector = TemplateSelector;
})(window.gl || ( window.gl = {}));
-
diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
index 2afef43f3d6..b801c10f168 100644
--- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 8db4f6a3b28..60840560dd3 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6
index d4f8f4b9420..efb22d38513 100644
--- a/app/assets/javascripts/boards/boards_bundle.js.es6
+++ b/app/assets/javascripts/boards/boards_bundle.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require vue
//= require vue-resource
//= require Sortable
@@ -5,7 +6,9 @@
//= require_tree ./stores
//= require_tree ./services
//= require_tree ./mixins
+//= require_tree ./filters
//= require ./components/board
+//= require ./components/board_sidebar
//= require ./components/new_list_dropdown
//= require ./vue_resource_interceptor
@@ -22,7 +25,8 @@ $(() => {
gl.IssueBoardsApp = new Vue({
el: $boardApp,
components: {
- 'board': gl.issueBoards.Board
+ 'board': gl.issueBoards.Board,
+ 'board-sidebar': gl.issueBoards.BoardSidebar
},
data: {
state: Store.state,
@@ -30,9 +34,15 @@ $(() => {
endpoint: $boardApp.dataset.endpoint,
boardId: $boardApp.dataset.boardId,
disabled: $boardApp.dataset.disabled === 'true',
- issueLinkBase: $boardApp.dataset.issueLinkBase
+ issueLinkBase: $boardApp.dataset.issueLinkBase,
+ detailIssue: Store.detail
},
init: Store.create.bind(Store),
+ computed: {
+ detailIssueVisible () {
+ return Object.keys(this.detailIssue.issue).length;
+ }
+ },
created () {
gl.boardService = new BoardService(this.endpoint, this.boardId);
},
diff --git a/app/assets/javascripts/boards/components/board.js.es6 b/app/assets/javascripts/boards/components/board.js.es6
index cacb36a897f..0e03d43872b 100644
--- a/app/assets/javascripts/boards/components/board.js.es6
+++ b/app/assets/javascripts/boards/components/board.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require ./board_blank_state
//= require ./board_delete
//= require ./board_list
@@ -21,6 +22,7 @@
},
data () {
return {
+ detailIssue: Store.detail,
filters: Store.state.filters,
showIssueForm: false
};
@@ -32,6 +34,26 @@
this.list.getIssues(true);
},
deep: true
+ },
+ detailIssue: {
+ handler () {
+ if (!Object.keys(this.detailIssue.issue).length) return;
+
+ const issue = this.list.findIssue(this.detailIssue.issue.id);
+
+ if (issue) {
+ const boardsList = document.querySelectorAll('.boards-list')[0];
+ const right = (this.$el.offsetLeft + this.$el.offsetWidth) - boardsList.offsetWidth;
+ const left = boardsList.scrollLeft - this.$el.offsetLeft;
+
+ if (right - boardsList.scrollLeft > 0) {
+ boardsList.scrollLeft = right;
+ } else if (left > 0) {
+ boardsList.scrollLeft = this.$el.offsetLeft;
+ }
+ }
+ },
+ deep: true
}
},
methods: {
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js.es6 b/app/assets/javascripts/boards/components/board_blank_state.js.es6
index ff90f2d6d75..885553690d3 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js.es6
+++ b/app/assets/javascripts/boards/components/board_blank_state.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6
index 4a7cfeaeab2..2f6c03e3538 100644
--- a/app/assets/javascripts/boards/components/board_card.js.es6
+++ b/app/assets/javascripts/boards/components/board_card.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
const Store = gl.issueBoards.BoardsStore;
@@ -12,6 +13,17 @@
disabled: Boolean,
index: Number
},
+ data () {
+ return {
+ showDetail: false,
+ detailIssue: Store.detail
+ };
+ },
+ computed: {
+ issueDetailVisible () {
+ return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
+ }
+ },
methods: {
filterByLabel (label, e) {
let labelToggleText = label.title;
@@ -37,6 +49,29 @@
$('.labels-filter .dropdown-toggle-text').text(labelToggleText);
Store.updateFiltersUrl();
+ },
+ mouseDown () {
+ this.showDetail = true;
+ },
+ mouseMove () {
+ if (this.showDetail) {
+ this.showDetail = false;
+ }
+ },
+ showIssue (e) {
+ const targetTagName = e.target.tagName.toLowerCase();
+
+ if (targetTagName === 'a' || targetTagName === 'button') return;
+
+ if (this.showDetail) {
+ this.showDetail = false;
+
+ if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
+ Store.detail.issue = {};
+ } else {
+ Store.detail.issue = this.issue;
+ }
+ }
}
}
});
diff --git a/app/assets/javascripts/boards/components/board_delete.js.es6 b/app/assets/javascripts/boards/components/board_delete.js.es6
index 34653cd48ef..c45e1926c5c 100644
--- a/app/assets/javascripts/boards/components/board_delete.js.es6
+++ b/app/assets/javascripts/boards/components/board_delete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6
index 7022a29e818..34fc7694241 100644
--- a/app/assets/javascripts/boards/components/board_list.js.es6
+++ b/app/assets/javascripts/boards/components/board_list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require ./board_card
//= require ./board_new_issue
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js.es6 b/app/assets/javascripts/boards/components/board_new_issue.js.es6
index a4fad422eca..7fc0bfd56f3 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.js.es6
+++ b/app/assets/javascripts/boards/components/board_new_issue.js.es6
@@ -1,4 +1,7 @@
+/* eslint-disable */
(() => {
+ const Store = gl.issueBoards.BoardsStore;
+
window.gl = window.gl || {};
gl.issueBoards.BoardNewIssue = Vue.extend({
@@ -27,13 +30,16 @@
const labels = this.list.label ? [this.list.label] : [];
const issue = new ListIssue({
title: this.title,
- labels
+ labels,
+ subscribed: true
});
this.list.newIssue(issue)
.then((data) => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$els.submitButton).enable();
+
+ Store.detail.issue = issue;
})
.catch(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6
new file mode 100644
index 00000000000..4928320d015
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6
@@ -0,0 +1,53 @@
+/* eslint-disable */
+(() => {
+ const Store = gl.issueBoards.BoardsStore;
+
+ window.gl = window.gl || {};
+ window.gl.issueBoards = window.gl.issueBoards || {};
+
+ gl.issueBoards.BoardSidebar = Vue.extend({
+ props: {
+ currentUser: Object
+ },
+ data() {
+ return {
+ detail: Store.detail,
+ issue: {}
+ };
+ },
+ computed: {
+ showSidebar () {
+ return Object.keys(this.issue).length;
+ }
+ },
+ watch: {
+ detail: {
+ handler () {
+ this.issue = this.detail.issue;
+ },
+ deep: true
+ },
+ issue () {
+ if (this.showSidebar) {
+ this.$nextTick(() => {
+ $('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0);
+ $('.right-sidebar').getNiceScroll().resize();
+ });
+ }
+ }
+ },
+ methods: {
+ closeSidebar () {
+ this.detail.issue = {};
+ }
+ },
+ ready () {
+ new IssuableContext(this.currentUser);
+ new MilestoneSelect();
+ new gl.DueDateSelectors();
+ new LabelsSelect();
+ new Sidebar();
+ new Subscription('.subscription');
+ }
+ });
+})();
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js.es6 b/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
index 6ccd83e2d84..fe1a6dc7ea0 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
$(() => {
const Store = gl.issueBoards.BoardsStore;
@@ -32,6 +33,7 @@ $(() => {
},
filterable: true,
selectable: true,
+ multiSelect: true,
clicked (label, $el, e) {
e.preventDefault();
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6
new file mode 100644
index 00000000000..9eceac4eddd
--- /dev/null
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6
@@ -0,0 +1,5 @@
+/* eslint-disable */
+Vue.filter('due-date', (value) => {
+ const date = new Date(value);
+ return $.datepicker.formatDate('M d, yy', date);
+});
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
index f629d45c587..db9a5a8e40a 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
@@ -21,8 +22,8 @@
fallbackClass: 'is-dragging',
fallbackOnBody: true,
ghostClass: 'is-ghost',
- filter: '.has-tooltip, .btn',
- delay: gl.issueBoards.touchEnabled ? 100 : 0,
+ filter: '.board-delete, .btn',
+ delay: gl.issueBoards.touchEnabled ? 100 : 50,
scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
scrollSpeed: 20,
onStart: gl.issueBoards.onStart,
diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6
index eb082103de9..21d735e8231 100644
--- a/app/assets/javascripts/boards/models/issue.js.es6
+++ b/app/assets/javascripts/boards/models/issue.js.es6
@@ -1,14 +1,21 @@
+/* eslint-disable */
class ListIssue {
constructor (obj) {
this.id = obj.iid;
this.title = obj.title;
this.confidential = obj.confidential;
+ this.dueDate = obj.due_date;
+ this.subscribed = obj.subscribed;
this.labels = [];
if (obj.assignee) {
this.assignee = new ListUser(obj.assignee);
}
+ if (obj.milestone) {
+ this.milestone = new ListMilestone(obj.milestone);
+ }
+
obj.labels.forEach((label) => {
this.labels.push(new ListLabel(label));
});
@@ -41,4 +48,21 @@ class ListIssue {
getLists () {
return gl.issueBoards.BoardsStore.state.lists.filter( list => list.findIssue(this.id) );
}
+
+ update (url) {
+ const data = {
+ issue: {
+ milestone_id: this.milestone ? this.milestone.id : null,
+ due_date: this.dueDate,
+ assignee_id: this.assignee ? this.assignee.id : null,
+ label_ids: this.labels.map( (label) => label.id )
+ }
+ };
+
+ if (!data.issue.label_ids.length) {
+ data.issue.label_ids = [''];
+ }
+
+ return Vue.http.patch(url, data);
+ }
}
diff --git a/app/assets/javascripts/boards/models/label.js.es6 b/app/assets/javascripts/boards/models/label.js.es6
index 583829552cd..0910fe9a854 100644
--- a/app/assets/javascripts/boards/models/label.js.es6
+++ b/app/assets/javascripts/boards/models/label.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class ListLabel {
constructor (obj) {
this.id = obj.id;
diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6
index 5d0a561cdba..b331a26fed5 100644
--- a/app/assets/javascripts/boards/models/list.js.es6
+++ b/app/assets/javascripts/boards/models/list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class List {
constructor (obj) {
this.id = obj.id;
diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6
new file mode 100644
index 00000000000..a48969e19c9
--- /dev/null
+++ b/app/assets/javascripts/boards/models/milestone.js.es6
@@ -0,0 +1,7 @@
+/* eslint-disable */
+class ListMilestone {
+ constructor (obj) {
+ this.id = obj.id;
+ this.title = obj.title;
+ }
+}
diff --git a/app/assets/javascripts/boards/models/user.js.es6 b/app/assets/javascripts/boards/models/user.js.es6
index 904b3a68507..583a973fc46 100644
--- a/app/assets/javascripts/boards/models/user.js.es6
+++ b/app/assets/javascripts/boards/models/user.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class ListUser {
constructor (user) {
this.id = user.id;
diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6
index b9c91cbf31e..f59a2ed7937 100644
--- a/app/assets/javascripts/boards/services/board_service.js.es6
+++ b/app/assets/javascripts/boards/services/board_service.js.es6
@@ -1,7 +1,6 @@
+/* eslint-disable */
class BoardService {
constructor (root, boardId) {
- Vue.http.options.root = root;
-
this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, {
generate: {
method: 'POST',
diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6
index bd07ee0c161..175e034afed 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js.es6
+++ b/app/assets/javascripts/boards/stores/boards_store.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
@@ -5,6 +6,9 @@
gl.issueBoards.BoardsStore = {
disabled: false,
state: {},
+ detail: {
+ issue: {}
+ },
moving: {
issue: {},
list: {}
@@ -58,12 +62,13 @@
removeBlankState () {
this.removeList('blank');
- $.cookie('issue_board_welcome_hidden', 'true', {
- expires: 365 * 10
+ Cookies.set('issue_board_welcome_hidden', 'true', {
+ expires: 365 * 10,
+ path: ''
});
},
welcomeIsHidden () {
- return $.cookie('issue_board_welcome_hidden') === 'true';
+ return Cookies.get('issue_board_welcome_hidden') === 'true';
},
removeList (id, type = 'blank') {
const list = this.findList('id', id, type);
diff --git a/app/assets/javascripts/boards/test_utils/simulate_drag.js b/app/assets/javascripts/boards/test_utils/simulate_drag.js
index 75f8b730195..039ca491cf5 100644
--- a/app/assets/javascripts/boards/test_utils/simulate_drag.js
+++ b/app/assets/javascripts/boards/test_utils/simulate_drag.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function () {
'use strict';
diff --git a/app/assets/javascripts/boards/vue_resource_interceptor.js.es6 b/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
index b5ff3a81ed5..80f137ca12e 100644
--- a/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
+++ b/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js
index 5fef9725178..5d4d23e26c6 100644
--- a/app/assets/javascripts/breakpoints.js
+++ b/app/assets/javascripts/breakpoints.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Breakpoints = (function() {
var BreakpointInstance, instance;
diff --git a/app/assets/javascripts/broadcast_message.js b/app/assets/javascripts/broadcast_message.js
index fceeff36728..576f4c76c1e 100644
--- a/app/assets/javascripts/broadcast_message.js
+++ b/app/assets/javascripts/broadcast_message.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
$(function() {
var previewPath;
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index f4c387a1a05..12e653f4122 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js
index f345ba0abe6..49f84581650 100644
--- a/app/assets/javascripts/build_artifacts.js
+++ b/app/assets/javascripts/build_artifacts.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.BuildArtifacts = (function() {
function BuildArtifacts() {
diff --git a/app/assets/javascripts/build_variables.js.es6 b/app/assets/javascripts/build_variables.js.es6
index 8d3e29794a1..0ecd20bc11e 100644
--- a/app/assets/javascripts/build_variables.js.es6
+++ b/app/assets/javascripts/build_variables.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
$(function(){
$('.reveal-variables').off('click').on('click',function(){
$('.js-build').toggle().niceScroll();
diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js
index 23cf5b519f4..fac5b4f17da 100644
--- a/app/assets/javascripts/commit.js
+++ b/app/assets/javascripts/commit.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Commit = (function() {
function Commit() {
diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js
index be24ee56aad..16d63729d31 100644
--- a/app/assets/javascripts/commit/file.js
+++ b/app/assets/javascripts/commit/file.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.CommitFile = (function() {
function CommitFile(file) {
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index e893491b19b..ffddce1297b 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ImageFile = (function() {
var prepareFrames;
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 9132089adcd..c765d233831 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.CommitsList = (function() {
function CommitsList() {}
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index 342ac0e8e69..b3f769d4129 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Compare = (function() {
function Compare(opts) {
diff --git a/app/assets/javascripts/compare_autocomplete.js.es6 b/app/assets/javascripts/compare_autocomplete.js.es6
index 9a2082d97e0..bd980f87e72 100644
--- a/app/assets/javascripts/compare_autocomplete.js.es6
+++ b/app/assets/javascripts/compare_autocomplete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.CompareAutocomplete = (function() {
function CompareAutocomplete() {
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 708ab08ffac..230a1b95c52 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ConfirmDangerModal = (function() {
function ConfirmDangerModal(form, text) {
diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js
index e23bda2fa4e..7808d7fe313 100644
--- a/app/assets/javascripts/copy_to_clipboard.js
+++ b/app/assets/javascripts/copy_to_clipboard.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require clipboard */
diff --git a/app/assets/javascripts/create_label.js.es6 b/app/assets/javascripts/create_label.js.es6
index c5f8c29242d..f20580b1279 100644
--- a/app/assets/javascripts/create_label.js.es6
+++ b/app/assets/javascripts/create_label.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function (w) {
class CreateLabelDropdown {
constructor ($el, namespacePath, projectPath) {
diff --git a/app/assets/javascripts/cycle_analytics.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
index 20791bab942..331f0209888 100644
--- a/app/assets/javascripts/cycle_analytics.js.es6
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require vue
((global) => {
@@ -6,7 +7,7 @@
const store = gl.cycleAnalyticsStore = {
isLoading: true,
hasError: false,
- isHelpDismissed: $.cookie(COOKIE_NAME),
+ isHelpDismissed: Cookies.get(COOKIE_NAME),
analytics: {}
};
@@ -75,9 +76,7 @@
dismissLanding() {
store.isHelpDismissed = true;
- $.cookie(COOKIE_NAME, true, {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set(COOKIE_NAME, true);
}
initDropdown() {
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index 8086c10ad6b..4ddafff428f 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Diff = (function() {
var UNFOLD_COUNT;
diff --git a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6
index 48bc7d77805..29a12a2395b 100644
--- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.CommentAndResolveBtn = Vue.extend({
props: {
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6 b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6
index ad80d1118df..983e554b9c1 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
JumpToDiscussion = Vue.extend({
mixins: [DiscussionMixins],
diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
index cdedfd1af15..bcc052c7c8c 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.ResolveBtn = Vue.extend({
props: {
diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
index 9e383b14a3e..24a99e23132 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.ResolveCount = Vue.extend({
mixins: [DiscussionMixins],
diff --git a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6
index 0a617034502..060034f049b 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.ResolveDiscussionBtn = Vue.extend({
props: {
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 22d9cf6c857..6149bfd052a 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require vue
//= require vue-resource
//= require_directory ./models
diff --git a/app/assets/javascripts/diff_notes/mixins/discussion.js.es6 b/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
index a05f885201d..7a929017f36 100644
--- a/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.DiscussionMixins = {
computed: {
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js.es6 b/app/assets/javascripts/diff_notes/models/discussion.js.es6
index 488714e4870..439f55520ef 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/models/discussion.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class DiscussionModel {
constructor (discussionId) {
this.id = discussionId;
diff --git a/app/assets/javascripts/diff_notes/models/note.js.es6 b/app/assets/javascripts/diff_notes/models/note.js.es6
index f2d2d389c38..d0541b02632 100644
--- a/app/assets/javascripts/diff_notes/models/note.js.es6
+++ b/app/assets/javascripts/diff_notes/models/note.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class NoteModel {
constructor (discussionId, noteId, canResolve, resolved, resolved_by) {
this.discussionId = discussionId;
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js.es6 b/app/assets/javascripts/diff_notes/services/resolve.js.es6
index 2a55f739b31..86953ce7ffb 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js.es6
+++ b/app/assets/javascripts/diff_notes/services/resolve.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
class ResolveServiceClass {
constructor() {
diff --git a/app/assets/javascripts/diff_notes/stores/comments.js.es6 b/app/assets/javascripts/diff_notes/stores/comments.js.es6
index 69522e1dac5..f42ca406bb1 100644
--- a/app/assets/javascripts/diff_notes/stores/comments.js.es6
+++ b/app/assets/javascripts/diff_notes/stores/comments.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.CommentsStore = {
state: {},
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index a1fe57562fa..ff8b8f6d0ae 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var Dispatcher;
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 4a6fea929c7..1a0aa9757ba 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require preview_markdown */
diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6
index 41925fcc8e3..fd7f961aab9 100644
--- a/app/assets/javascripts/due_date_select.js.es6
+++ b/app/assets/javascripts/due_date_select.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function(global) {
class DueDateSelect {
constructor({ $dropdown, $loading } = {}) {
@@ -41,7 +42,12 @@
defaultDate: $("input[name='" + this.fieldName + "']").val(),
altField: "input[name='" + this.fieldName + "']",
onSelect: () => {
- return this.saveDueDate(true);
+ if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
+ gl.issueBoards.BoardsStore.detail.issue.dueDate = $(`input[name='${this.fieldName}']`).val();
+ this.updateIssueBoardIssue();
+ } else {
+ return this.saveDueDate(true);
+ }
}
});
}
@@ -49,8 +55,14 @@
initRemoveDueDate() {
this.$block.on('click', '.js-remove-due-date', (e) => {
e.preventDefault();
- $("input[name='" + this.fieldName + "']").val('');
- return this.saveDueDate(false);
+
+ if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
+ gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
+ this.updateIssueBoardIssue();
+ } else {
+ $("input[name='" + this.fieldName + "']").val('');
+ return this.saveDueDate(false);
+ }
});
}
@@ -83,6 +95,18 @@
this.datePayload = datePayload;
}
+ updateIssueBoardIssue () {
+ this.$loading.fadeIn();
+ this.$dropdown.trigger('loading.gl.dropdown');
+ this.$selectbox.hide();
+ this.$value.css('display', '');
+
+ gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update'))
+ .then(() => {
+ this.$loading.fadeOut();
+ });
+ }
+
submitSelectedDate(isDropdown) {
return $.ajax({
type: 'PUT',
diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js
index 24f9e00097c..4c9e219aa43 100644
--- a/app/assets/javascripts/extensions/array.js
+++ b/app/assets/javascripts/extensions/array.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
Array.prototype.first = function() {
return this[0];
}
diff --git a/app/assets/javascripts/extensions/element.js.es6 b/app/assets/javascripts/extensions/element.js.es6
new file mode 100644
index 00000000000..c74fc9ad074
--- /dev/null
+++ b/app/assets/javascripts/extensions/element.js.es6
@@ -0,0 +1,7 @@
+/* eslint-disable */
+Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatches;
+
+Element.prototype.closest = function closest(selector, selectedElement = this) {
+ if (!selectedElement) return;
+ return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement);
+};
diff --git a/app/assets/javascripts/extensions/jquery.js b/app/assets/javascripts/extensions/jquery.js
index 4978e24949c..623a80b7053 100644
--- a/app/assets/javascripts/extensions/jquery.js
+++ b/app/assets/javascripts/extensions/jquery.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Disable an element and add the 'disabled' Bootstrap class
(function() {
$.fn.extend({
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 3fb3b1a8b51..732136f1f2c 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index c8a02d6fa15..46e272c3311 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Flash = (function() {
var hideFlash;
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 845313b6b38..31df51ac03a 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Creates the variables for setting up GFM auto-completion
(function() {
if (window.GitLab == null) {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 53762f2965c..98e43c4d088 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
@@ -208,7 +209,7 @@
FILTER_INPUT = '.dropdown-input .dropdown-input-field';
function GitLabDropdown(el1, options) {
- var ref, ref1, ref2, ref3, searchFields, selector, self;
+ var searchFields, selector, self;
this.el = el1;
this.options = options;
this.updateLabel = bind(this.updateLabel, this);
@@ -219,7 +220,11 @@
selector = $(this.el).data("target");
this.dropdown = selector != null ? $(selector) : $(this.el).parent();
// Set Defaults
- ref = this.options, this.filterInput = (ref1 = ref.filterInput) != null ? ref1 : this.getElement(FILTER_INPUT), this.highlight = (ref2 = ref.highlight) != null ? ref2 : false, this.filterInputBlur = (ref3 = ref.filterInputBlur) != null ? ref3 : true;
+ this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT);
+ this.highlight = !!this.options.highlight
+ this.filterInputBlur = this.options.filterInputBlur != null
+ ? this.options.filterInputBlur
+ : true;
// If no input is passed create a default one
self = this;
// If selector was passed
@@ -234,6 +239,7 @@
this.fullData = this.options.data;
currentIndex = -1;
this.parseData(this.options.data);
+ this.focusTextInput();
} else {
this.remote = new GitLabDropdownRemote(this.options.data, {
dataType: this.options.dataType,
@@ -242,6 +248,7 @@
return function(data) {
_this.fullData = data;
_this.parseData(_this.fullData);
+ _this.focusTextInput();
if (_this.options.filterable && _this.filter && _this.filter.input) {
return _this.filter.input.trigger('input');
}
@@ -418,7 +425,9 @@
var $target;
if (this.options.multiSelect) {
$target = $(e.target);
- if ($target && !$target.hasClass('dropdown-menu-close') && !$target.hasClass('dropdown-menu-close-icon') && !$target.data('is-link')) {
+ if ($target && !$target.hasClass('dropdown-menu-close') &&
+ !$target.hasClass('dropdown-menu-close-icon') &&
+ !$target.data('is-link')) {
e.stopPropagation();
return false;
} else {
@@ -445,9 +454,8 @@
contentHtml = $('.dropdown-content', this.dropdown).html();
if (this.remote && contentHtml === "") {
this.remote.execute();
- }
- if (this.options.filterable) {
- this.filterInput.focus();
+ } else {
+ this.focusTextInput();
}
if (this.options.showMenuAbove) {
@@ -549,6 +557,8 @@
value = this.options.id ? this.options.id(data) : data.id;
fieldName = this.options.fieldName;
+ if (value) { value = value.toString().replace(/'/g, '\\\'') };
+
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']");
if (field.length) {
selected = true;
@@ -620,8 +630,21 @@
selectedObject = this.renderedData[selectedIndex];
}
}
+
+ if (this.options.vue) {
+ if (el.hasClass(ACTIVE_CLASS)) {
+ el.removeClass(ACTIVE_CLASS);
+ } else {
+ el.addClass(ACTIVE_CLASS);
+ }
+
+ return selectedObject;
+ }
+
field = [];
- value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
+ value = this.options.id
+ ? this.options.id(selectedObject, el)
+ : selectedObject.id;
if (isInput) {
field = $(this.el);
} else if(value) {
@@ -669,6 +692,10 @@
return selectedObject;
};
+ GitLabDropdown.prototype.focusTextInput = function() {
+ if (this.options.filterable) { this.filterInput.focus() }
+ }
+
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
var $input;
// Create hidden input for form
diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6
index 8e8f9f29ab3..be6c3ec274f 100644
--- a/app/assets/javascripts/gl_field_errors.js.es6
+++ b/app/assets/javascripts/gl_field_errors.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
/*
* This class overrides the browser's validation error bubbles, displaying custom
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index 2703adc0705..742807d93ad 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.GLForm = (function() {
function GLForm(form) {
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index 4886da9f21f..e103748d499 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,5 +1,6 @@
+/* eslint-disable */
// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
diff --git a/app/assets/javascripts/graphs/stat_graph.js b/app/assets/javascripts/graphs/stat_graph.js
index f041980bc19..b796a9abb49 100644
--- a/app/assets/javascripts/graphs/stat_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.StatGraph = (function() {
function StatGraph() {}
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js
index 927d241b357..818bff0c413 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require d3 */
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
index 7d9d4d7c679..dea26a3f1e1 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require d3 */
@@ -29,8 +30,7 @@
ContributorsGraph.set_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
- var ref, ref1;
- return d.commits = (ref = (ref1 = d.commits) != null ? ref1 : d.additions) != null ? ref : d.deletions;
+ return d.commits = d.commits || d.additions || d.deletions;
})
];
};
@@ -44,8 +44,7 @@
ContributorsGraph.init_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
- var ref, ref1;
- return d.commits = (ref = (ref1 = d.commits) != null ? ref1 : d.additions) != null ? ref : d.deletions;
+ return d.commits = d.commits || d.additions || d.deletions;
})
];
};
@@ -147,9 +146,8 @@
return this.area = d3.svg.area().x(function(d) {
return x(d.date);
}).y0(this.height).y1(function(d) {
- var ref, ref1, xa;
- xa = d.commits = (ref = (ref1 = d.commits) != null ? ref1 : d.additions) != null ? ref : d.deletions;
- return y(xa);
+ d.commits = d.commits || d.additions || d.deletions;
+ return y(d.commits);
}).interpolate("basis");
};
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
index 0d240bed8b6..362a77e868f 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
window.ContributorsStatGraphUtil = {
parse_log: function(log) {
diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js
index c28ce86d7af..774477dc7a9 100644
--- a/app/assets/javascripts/group_avatar.js
+++ b/app/assets/javascripts/group_avatar.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.GroupAvatar = (function() {
function GroupAvatar() {
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index 5f06186504b..b275620c799 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var slice = [].slice;
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
new file mode 100644
index 00000000000..81fcaf06430
--- /dev/null
+++ b/app/assets/javascripts/header.js
@@ -0,0 +1,10 @@
+/* eslint-disable */
+(function() {
+
+ $(document).on('todo:toggle', function(e, count) {
+ var $todoPendingCount = $('.todos-pending-count');
+ $todoPendingCount.text(gl.text.addDelimiter(count));
+ $todoPendingCount.toggleClass('hidden', count === 0);
+ });
+
+})();
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 4aced1e618f..c53f7c88aa2 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ImporterStatus = (function() {
function ImporterStatus(jobs_url, import_url) {
diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6
index 57f7e4ef230..8fc498be27d 100644
--- a/app/assets/javascripts/issuable.js.es6
+++ b/app/assets/javascripts/issuable.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var issuable_created;
@@ -15,16 +16,61 @@
return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>');
},
initSearch: function() {
+ const $searchInput = $('#issuable_search');
+
+ Issuable.initSearchState($searchInput);
+
// `immediate` param set to false debounces on the `trailing` edge, lets user finish typing
- const debouncedExecSearch = _.debounce(Issuable.executeSearch, 500, false);
+ const debouncedExecSearch = _.debounce(Issuable.executeSearch, 1000, false);
- $('#issuable_search').off('keyup').on('keyup', debouncedExecSearch);
+ $searchInput.off('keyup').on('keyup', debouncedExecSearch);
// ensures existing filters are preserved when manually submitted
- $('#issue_search_form').on('submit', (e) => {
+ $('#issuable_search_form').on('submit', (e) => {
e.preventDefault();
debouncedExecSearch(e);
});
+
+ },
+ initSearchState: function($searchInput) {
+ const currentSearchVal = $searchInput.val();
+
+ Issuable.searchState = {
+ elem: $searchInput,
+ current: currentSearchVal
+ };
+
+ Issuable.maybeFocusOnSearch();
+ },
+ accessSearchPristine: function(set) {
+ // store reference to previous value to prevent search on non-mutating keyup
+ const state = Issuable.searchState;
+ const currentSearchVal = state.elem.val();
+
+ if (set) {
+ state.current = currentSearchVal;
+ } else {
+ return state.current === currentSearchVal;
+ }
+ },
+ maybeFocusOnSearch: function() {
+ const currentSearchVal = Issuable.searchState.current;
+ if (currentSearchVal && currentSearchVal !== '') {
+ const queryLength = currentSearchVal.length;
+ const $searchInput = Issuable.searchState.elem;
+
+ /* The following ensures that the cursor is initially placed at
+ * the end of search input when focus is applied. It accounts
+ * for differences in browser implementations of `setSelectionRange`
+ * and cursor placement for elements in focus.
+ */
+ $searchInput.focus();
+ if ($searchInput.setSelectionRange) {
+ $searchInput.setSelectionRange(queryLength, queryLength);
+ } else {
+ $searchInput.val(currentSearchVal);
+ }
+ }
},
executeSearch: function(e) {
const $search = $('#issuable_search');
@@ -32,6 +78,11 @@
const $searchValue = $search.val();
const $filtersForm = $('.js-filter-form');
const $input = $(`input[name='${$searchName}']`, $filtersForm);
+ const isPristine = Issuable.accessSearchPristine();
+
+ if (isPristine) {
+ return;
+ }
if (!$input.length) {
$filtersForm.append(`<input type='hidden' name='${$searchName}' value='${_.escape($searchValue)}'/>`);
diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js
index 8147e83ffe8..fae49ee6144 100644
--- a/app/assets/javascripts/issuable_context.js
+++ b/app/assets/javascripts/issuable_context.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.IssuableContext = (function() {
function IssuableContext(currentUser) {
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index b7f92ae9883..849b45756ee 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -44,8 +45,8 @@
};
IssuableForm.prototype.handleSubmit = function() {
- var ref, ref1;
- if (((ref = parseInt((ref1 = this.issueMoveField) != null ? ref1.val() : void 0)) != null ? ref : 0) > 0) {
+ var fieldId = (this.issueMoveField != null) ? this.issueMoveField.val() : null;
+ if ((parseInt(fieldId) || 0) > 0) {
if (!confirm(this.issueMoveConfirmMsg)) {
return false;
}
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 261bf6137c2..67ace697936 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require flash */
/*= require jquery.waitforimages */
@@ -94,7 +95,11 @@
return $.ajax({
type: 'PATCH',
url: $('form.js-issuable-update').attr('action'),
- data: patchData
+ 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
diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js
index 076e3972944..d7262e5eb74 100644
--- a/app/assets/javascripts/issue_status_select.js
+++ b/app/assets/javascripts/issue_status_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.IssueStatusSelect = (function() {
function IssueStatusSelect() {
diff --git a/app/assets/javascripts/issues_bulk_assignment.js.es6 b/app/assets/javascripts/issues_bulk_assignment.js.es6
index 0808f538f01..9697fb33566 100644
--- a/app/assets/javascripts/issues_bulk_assignment.js.es6
+++ b/app/assets/javascripts/issues_bulk_assignment.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class IssuableBulkActions {
diff --git a/app/assets/javascripts/label_manager.js.es6 b/app/assets/javascripts/label_manager.js.es6
index bc68e53504f..175623e7448 100644
--- a/app/assets/javascripts/label_manager.js.es6
+++ b/app/assets/javascripts/label_manager.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class LabelManager {
diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js
index cb16e2ba814..3033e8ca5c2 100644
--- a/app/assets/javascripts/labels.js
+++ b/app/assets/javascripts/labels.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index b4f6e70f694..c334e3e0c02 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
@@ -22,7 +23,7 @@
abilityName = $dropdown.data('ability-name');
$selectbox = $dropdown.closest('.selectbox');
$block = $selectbox.closest('.block');
- $form = $dropdown.closest('form');
+ $form = $dropdown.closest('form, .js-issuable-update');
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
$value = $block.find('.value');
@@ -317,6 +318,7 @@
}
},
multiSelect: $dropdown.hasClass('js-multiselect'),
+ vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(label, $el, e) {
var isIssueIndex, isMRIndex, page;
_this.enableBulkLabelDropdown();
@@ -334,7 +336,7 @@
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = page === 'projects:merge_requests:index';
- if ($('html').hasClass('issue-boards-page')) {
+ if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
if (label.isAny) {
gl.issueBoards.BoardsStore.state.filters['label_name'] = [];
}
@@ -362,6 +364,30 @@
else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
}
+ else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ if ($el.hasClass('is-active')) {
+ gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({
+ id: label.id,
+ title: label.title,
+ color: label.color[0],
+ textColor: '#fff'
+ }));
+ }
+ else {
+ var labels = gl.issueBoards.BoardsStore.detail.issue.labels;
+ labels = labels.filter(function (selectedLabel) {
+ return selectedLabel.id !== label.id;
+ });
+ gl.issueBoards.BoardsStore.detail.issue.labels = labels;
+ }
+
+ $loading.fadeIn();
+
+ gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ .then(function () {
+ $loading.fadeOut();
+ });
+ }
else {
if ($dropdown.hasClass('js-multiselect')) {
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index 8e2fc0d1479..6b4edf02f4d 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var hideEndFade;
diff --git a/app/assets/javascripts/lib/ace.js b/app/assets/javascripts/lib/ace.js
index 4cdf99cae72..b1718e89d3d 100644
--- a/app/assets/javascripts/lib/ace.js
+++ b/app/assets/javascripts/lib/ace.js
@@ -1,2 +1,3 @@
+/* eslint-disable */
/*= require ace-rails-ap */
/*= require ace/ext-searchbox */
diff --git a/app/assets/javascripts/lib/chart.js b/app/assets/javascripts/lib/chart.js
index d9b07c10a49..e1dfdae97de 100644
--- a/app/assets/javascripts/lib/chart.js
+++ b/app/assets/javascripts/lib/chart.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require Chart */
diff --git a/app/assets/javascripts/lib/cropper.js b/app/assets/javascripts/lib/cropper.js
index a88e640f298..155e30cc462 100644
--- a/app/assets/javascripts/lib/cropper.js
+++ b/app/assets/javascripts/lib/cropper.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require cropper */
diff --git a/app/assets/javascripts/lib/d3.js b/app/assets/javascripts/lib/d3.js
index ee1baf54803..0c9c2787077 100644
--- a/app/assets/javascripts/lib/d3.js
+++ b/app/assets/javascripts/lib/d3.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require d3 */
diff --git a/app/assets/javascripts/lib/raphael.js b/app/assets/javascripts/lib/raphael.js
index 6df427bc2b1..cc445db274b 100644
--- a/app/assets/javascripts/lib/raphael.js
+++ b/app/assets/javascripts/lib/raphael.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require raphael */
/*= require g.raphael */
diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js
index d36efdabc93..a68edab2aad 100644
--- a/app/assets/javascripts/lib/utils/animate.js
+++ b/app/assets/javascripts/lib/utils/animate.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
if (w.gl == null) {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index b170e26eebf..21efe2d76dd 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
var base;
@@ -43,6 +44,14 @@
parser.href = url;
return parser;
};
+
+ gl.utils.cleanupBeforeFetch = function() {
+ // Unbind scroll events
+ $(document).off('scroll');
+ // Close any open tooltips
+ $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy');
+ };
+
return jQuery.timefor = function(time, suffix, expiredLabel) {
var suffixFromNow, timefor;
if (!time) {
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 8fdf4646cd8..59e526ed623 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
var base;
diff --git a/app/assets/javascripts/lib/utils/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js
index cc17aa7d3d1..de76cdd2ea7 100644
--- a/app/assets/javascripts/lib/utils/jquery.timeago.js
+++ b/app/assets/javascripts/lib/utils/jquery.timeago.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/**
* Timeago is a jQuery plugin that makes it easy to support automatically
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index 5b338b00d76..dafc006d2e5 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
var notificationGranted, notifyMe, notifyPermissions;
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index d761a844be9..98f9815ff05 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
var base;
@@ -7,6 +8,9 @@
if ((base = w.gl).text == null) {
base.text = {};
}
+ gl.text.addDelimiter = function(text) {
+ return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
+ }
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
diff --git a/app/assets/javascripts/lib/utils/type_utility.js b/app/assets/javascripts/lib/utils/type_utility.js
index dc30babd645..4fd1e3fc1d3 100644
--- a/app/assets/javascripts/lib/utils/type_utility.js
+++ b/app/assets/javascripts/lib/utils/type_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
var base;
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index b8d52becb3f..44a66a915e3 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
(function(w) {
var base;
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index 93daea1dce7..ea5a60bb78e 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// LineHighlighter
//
// Handles single- and multi-line selection and highlight for blob views.
diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js
index 7d8eef1b495..d4f86534f0c 100644
--- a/app/assets/javascripts/logo.js
+++ b/app/assets/javascripts/logo.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
Turbolinks.enableProgressBar();
diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js
index e1532fd9ec4..0bd90c57396 100644
--- a/app/assets/javascripts/member_expiration_date.js
+++ b/app/assets/javascripts/member_expiration_date.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
// Add datepickers to all `js-access-expiration-date` elements. If those elements are
// children of an element with the `clearable-input` class, and have a sibling
diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6
index 2bdd0f7a637..371abd09e78 100644
--- a/app/assets/javascripts/members.js.es6
+++ b/app/assets/javascripts/members.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.gl = w.gl || {};
diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
index 5012bdfe997..6da3942ea52 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6 b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
index b4be1c8988d..23c4618af70 100644
--- a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6 b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6
index 8b0a8ab2073..797850262cc 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6 b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
index eb4cc6a9dac..1b3e9901f1e 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
index da2fb8b1323..8a7519b0786 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
index 5c5c65f29d4..f94e51e783c 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
@@ -1,7 +1,8 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
- const diffViewType = $.cookie('diff_view');
+ const diffViewType = Cookies.get('diff_view');
const HEAD_HEADER_TEXT = 'HEAD//our changes';
const ORIGIN_HEADER_TEXT = 'origin//their changes';
const HEAD_BUTTON_TITLE = 'Use ours';
@@ -180,9 +181,7 @@
this.state.diffView = viewType;
this.state.isParallel = viewType === VIEW_TYPES.PARALLEL;
- $.cookie('diff_view', viewType, {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set('diff_view', viewType);
},
getHeadHeaderLine(id) {
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
index 7fd3749b3e2..222a5dcfc2e 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require vue
//= require ./merge_conflict_store
//= require ./merge_conflict_service
diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6 b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
index 114a2c5b305..c8de586aa21 100644
--- a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6 b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
index b846a90ab2a..88c3a20ce13 100644
--- a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 02ff5a382e2..d3bd1e846c1 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require jquery.waitforimages */
/*= require task_list */
@@ -96,7 +97,11 @@
return $.ajax({
type: 'PATCH',
url: $('form.js-issuable-update').attr('action'),
- data: patchData
+ 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
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 3dde979185b..860ee5df57e 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -1,9 +1,10 @@
+/* eslint-disable */
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the MergeRequests#show page.
//
-/*= require jquery.cookie */
+/*= require js.cookie */
//
// ### Example Markup
@@ -237,8 +238,11 @@
_this.expandViewContainer();
}
_this.diffsLoaded = true;
- _this.scrollToElement("#diffs");
- _this.highlighSelectedLine();
+ var anchoredDiff = gl.utils.getLocationHash();
+ if (anchoredDiff) _this.openAnchoredDiff(anchoredDiff, function() {
+ _this.scrollToElement("#diffs");
+ _this.highlighSelectedLine();
+ });
_this.filesCommentButton = $('.files .diff-file').filesCommentButton();
return $(document).off('click', '.diff-line-num a').on('click', '.diff-line-num a', function(e) {
e.preventDefault();
@@ -251,6 +255,17 @@
});
};
+ MergeRequestTabs.prototype.openAnchoredDiff = function(anchoredDiff, cb) {
+ var diffTitle = $('#file-path-' + anchoredDiff);
+ var diffFile = diffTitle.closest('.diff-file');
+ var nothingHereBlock = $('.nothing-here-block:visible', diffFile);
+ if (nothingHereBlock.length) {
+ diffFile.singleFileDiff(true, cb);
+ } else {
+ cb();
+ }
+ };
+
MergeRequestTabs.prototype.highlighSelectedLine = function() {
var $diffLine, diffLineTop, hashClassString, locationHash, navBarHeight;
$('.hll').removeClass('hll');
@@ -368,7 +383,7 @@
MergeRequestTabs.prototype.expandView = function() {
var $gutterIcon;
- if ($.cookie('collapsed_gutter') === 'true') {
+ if (Cookies.get('collapsed_gutter') === 'true') {
return;
}
$gutterIcon = $('.js-sidebar-toggle i:visible');
diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6
index 3ff6851d59b..3a2fe454b68 100644
--- a/app/assets/javascripts/merge_request_widget.js.es6
+++ b/app/assets/javascripts/merge_request_widget.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
diff --git a/app/assets/javascripts/merged_buttons.js b/app/assets/javascripts/merged_buttons.js
index 1fed38661a2..7ad86d8c084 100644
--- a/app/assets/javascripts/merged_buttons.js
+++ b/app/assets/javascripts/merged_buttons.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index bc1a99057d9..9299c96e8ea 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Milestone = (function() {
Milestone.updateIssue = function(li, issue_url, data) {
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index cee42633c79..c909b53dc21 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.MilestoneSelect = (function() {
function MilestoneSelect(currentProject) {
@@ -101,6 +102,7 @@
// display:block overrides the hide-collapse rule
return $value.css('display', '');
},
+ vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(selected, $el, e) {
var data, isIssueIndex, isMRIndex, page;
page = $('body').data('page');
@@ -110,7 +112,7 @@
e.preventDefault();
return;
}
- if ($('html').hasClass('issue-boards-page')) {
+ if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = selected.name;
gl.issueBoards.BoardsStore.updateFiltersUrl();
e.preventDefault();
@@ -123,6 +125,24 @@
return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ if (selected.id !== -1) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'milestone', new ListMilestone({
+ id: selected.id,
+ title: selected.name
+ }));
+ } else {
+ Vue.delete(gl.issueBoards.BoardsStore.detail.issue, 'milestone');
+ }
+
+ $dropdown.trigger('loading.gl.dropdown');
+ $loading.fadeIn();
+
+ gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ .then(function () {
+ $dropdown.trigger('loaded.gl.dropdown');
+ $loading.fadeOut();
+ });
} else {
selected = $selectbox.find('input[type="hidden"]').val();
data = {};
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 10f4fd106d8..d1168227b77 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index 91132af273a..74dbeb94741 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/network/network.js b/app/assets/javascripts/network/network.js
index 7baebcd100a..8898e7ace43 100644
--- a/app/assets/javascripts/network/network.js
+++ b/app/assets/javascripts/network/network.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Network = (function() {
function Network(opts) {
diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js
index 67c3e645364..42d6799c82f 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -1,5 +1,6 @@
+/* eslint-disable */
// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 20aa2fced27..0e643b0ff14 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js
index 21bf8867f7b..acb529023fa 100644
--- a/app/assets/javascripts/new_commit_form.js
+++ b/app/assets/javascripts/new_commit_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 866a04d3e21..4976eef2896 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require autosave */
/*= require autosize */
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index a41e9d3fabe..ef3f2c6ae73 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.NotificationsDropdown = (function() {
function NotificationsDropdown() {
diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js
index 6b2ef17ef6b..6fbec8efe9b 100644
--- a/app/assets/javascripts/notifications_form.js
+++ b/app/assets/javascripts/notifications_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js
index b81ed50cb48..2e4dc62273e 100644
--- a/app/assets/javascripts/pager.js
+++ b/app/assets/javascripts/pager.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Pager = {
init: function(limit, preload, disable, callback) {
diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6
index a7624de6089..e6fada5c84c 100644
--- a/app/assets/javascripts/pipelines.js.es6
+++ b/app/assets/javascripts/pipelines.js.es6
@@ -1,37 +1,41 @@
+/* eslint-disable */
((global) => {
class Pipelines {
constructor() {
- $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph);
+ this.initGraphToggle();
this.addMarginToBuildColumns();
}
- toggleGraph() {
- const $pipelineBtn = $(this).closest('.toggle-pipeline-btn');
- const $pipelineGraph = $(this).closest('.row-content-block').next('.pipeline-graph');
- const $btnText = $(this).find('.toggle-btn-text');
- const graphCollapsed = $pipelineGraph.hasClass('graph-collapsed');
-
- $($pipelineBtn).add($pipelineGraph).toggleClass('graph-collapsed');
-
+ initGraphToggle() {
+ this.pipelineGraph = document.querySelector('.pipeline-graph');
+ this.toggleButton = document.querySelector('.toggle-pipeline-btn');
+ this.toggleButtonText = this.toggleButton.querySelector('.toggle-btn-text');
+ this.toggleButton.addEventListener('click', this.toggleGraph.bind(this));
+ }
- graphCollapsed ? $btnText.text('Hide') : $btnText.text('Expand')
+ toggleGraph() {
+ const graphCollapsed = this.pipelineGraph.classList.contains('graph-collapsed');
+ this.toggleButton.classList.toggle('graph-collapsed');
+ this.pipelineGraph.classList.toggle('graph-collapsed');
+ this.toggleButtonText.textContent = graphCollapsed ? 'Hide' : 'Expand';
}
addMarginToBuildColumns() {
- const $secondChildBuildNode = $('.build:nth-child(2)');
- if ($secondChildBuildNode.length) {
- const $firstChildBuildNode = $secondChildBuildNode.prev('.build');
- const $multiBuildColumn = $secondChildBuildNode.closest('.stage-column');
- const $previousColumn = $multiBuildColumn.prev('.stage-column');
- $multiBuildColumn.addClass('left-margin');
- $firstChildBuildNode.addClass('left-connector');
- $previousColumn.each(function() {
- $this = $(this);
- if ($('.build', $this).length === 1) $this.addClass('no-margin');
- });
+ const secondChildBuildNodes = this.pipelineGraph.querySelectorAll('.build:nth-child(2)');
+ for (buildNodeIndex in secondChildBuildNodes) {
+ const buildNode = secondChildBuildNodes[buildNodeIndex];
+ const firstChildBuildNode = buildNode.previousElementSibling;
+ if (!firstChildBuildNode || !firstChildBuildNode.matches('.build')) continue;
+ const multiBuildColumn = buildNode.closest('.stage-column');
+ const previousColumn = multiBuildColumn.previousElementSibling;
+ if (!previousColumn || !previousColumn.matches('.stage-column')) continue;
+ multiBuildColumn.classList.add('left-margin');
+ firstChildBuildNode.classList.add('left-connector');
+ const columnBuilds = previousColumn.querySelectorAll('.build');
+ if (columnBuilds.length === 1) previousColumn.classList.add('no-margin');
}
- $('.pipeline-graph').removeClass('hidden');
+ this.pipelineGraph.classList.remove('hidden');
}
}
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 5200487814f..f2a45a18bed 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
diff --git a/app/assets/javascripts/profile/gl_crop.js.es6 b/app/assets/javascripts/profile/gl_crop.js.es6
index a1b0126e857..6da6c1d0295 100644
--- a/app/assets/javascripts/profile/gl_crop.js.es6
+++ b/app/assets/javascripts/profile/gl_crop.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
// Matches everything but the file name
diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6
index b2307be73ad..73858388261 100644
--- a/app/assets/javascripts/profile/profile.js.es6
+++ b/app/assets/javascripts/profile/profile.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class Profile {
diff --git a/app/assets/javascripts/profile/profile_bundle.js b/app/assets/javascripts/profile/profile_bundle.js
index d6e4d9f7ad8..22bee0f6187 100644
--- a/app/assets/javascripts/profile/profile_bundle.js
+++ b/app/assets/javascripts/profile/profile_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require_tree . */
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index a6c015299a0..2d0c6b16699 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Project = (function() {
function Project() {
@@ -23,16 +24,12 @@
return $(this).parents('form').submit();
});
$('.hide-no-ssh-message').on('click', function(e) {
- $.cookie('hide_no_ssh_message', 'false', {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set('hide_no_ssh_message', 'false');
$(this).parents('.no-ssh-key-message').remove();
return e.preventDefault();
});
$('.hide-no-password-message').on('click', function(e) {
- $.cookie('hide_no_password_message', 'false', {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set('hide_no_password_message', 'false');
$(this).parents('.no-password-message').remove();
return e.preventDefault();
});
@@ -82,7 +79,7 @@
if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header);
} else {
- link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
+ link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', ref);
return $('<li />').append(link);
}
},
diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js
index 277e71523d5..61877c6616d 100644
--- a/app/assets/javascripts/project_avatar.js
+++ b/app/assets/javascripts/project_avatar.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ProjectAvatar = (function() {
function ProjectAvatar() {
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index b8347367717..ddac5ed83e1 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js
index d2261c51f35..fd95f8f2c19 100644
--- a/app/assets/javascripts/project_fork.js
+++ b/app/assets/javascripts/project_fork.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ProjectFork = (function() {
function ProjectFork() {
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index c61b0cf2fde..f1c4a9fe542 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ProjectImport = (function() {
function ProjectImport() {
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js
index 478e82aa14d..40575caa57f 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/project_new.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 4239ed2f889..b74b4ae68ff 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ProjectSelect = (function() {
function ProjectSelect() {
diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js
index c8cfc9a9ba8..21650f5f67a 100644
--- a/app/assets/javascripts/project_show.js
+++ b/app/assets/javascripts/project_show.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ProjectShow = (function() {
function ProjectShow() {}
diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js
index 04fb49552e8..3458cd89ae2 100644
--- a/app/assets/javascripts/projects_list.js
+++ b/app/assets/javascripts/projects_list.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.ProjectsList = {
init: function() {
diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
index 7aeb5f92514..2d60947a666 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
index 46beca469b9..c45c9d8ff22 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
index 983322cbecc..e3f226e9a2a 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class ProtectedBranchDropdown {
constructor(options) {
this.onSelect = options.onSelect;
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
index 15a6dca2875..ac3142ffb07 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
index 9ff0fd12c76..705378a364d 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js
index 15b3affd469..17e34163831 100644
--- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js
+++ b/app/assets/javascripts/protected_branches/protected_branches_bundle.js
@@ -1 +1,2 @@
+/* eslint-disable */
/*= require_tree . */
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index e3d5f413c77..df38937858f 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -5,15 +6,24 @@
function Sidebar(currentUser) {
this.toggleTodo = bind(this.toggleTodo, this);
this.sidebar = $('aside');
+ this.removeListeners();
this.addEventListeners();
}
+ Sidebar.prototype.removeListeners = function () {
+ this.sidebar.off('click', '.sidebar-collapsed-icon');
+ $('.dropdown').off('hidden.gl.dropdown');
+ $('.dropdown').off('loading.gl.dropdown');
+ $('.dropdown').off('loaded.gl.dropdown');
+ $(document).off('click', '.js-sidebar-toggle');
+ }
+
Sidebar.prototype.addEventListeners = function() {
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).off('click', '.js-sidebar-toggle').on('click', '.js-sidebar-toggle', function(e, triggered) {
+ $(document).on('click', '.js-sidebar-toggle', function(e, triggered) {
var $allGutterToggleIcons, $this, $thisIcon;
e.preventDefault();
$this = $(this);
@@ -29,9 +39,7 @@
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
}
if (!triggered) {
- return $.cookie("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'), {
- path: gon.relative_url_root || '/'
- });
+ return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
}
});
return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
@@ -74,16 +82,11 @@
};
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
- var $todoPendingCount;
- $todoPendingCount = $('.todos-pending-count');
- $todoPendingCount.text(data.count);
+ $(document).trigger('todo:toggle', data.count);
+
$btn.enable();
$todoLoading.addClass('hidden');
- if (data.count === 0) {
- $todoPendingCount.addClass('hidden');
- } else {
- $todoPendingCount.removeClass('hidden');
- }
+
if (data.delete_path != null) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
return $btnText.text($btn.data('mark-text'));
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index 8074a94f33e..6c2389f202f 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Search = (function() {
function Search() {
diff --git a/app/assets/javascripts/search_autocomplete.js.es6 b/app/assets/javascripts/search_autocomplete.js.es6
index b4c6226dc68..5fa94556501 100644
--- a/app/assets/javascripts/search_autocomplete.js.es6
+++ b/app/assets/javascripts/search_autocomplete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
const KEYCODE = {
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index 3aa8536d40a..8d8ab6dda5e 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js
index b931eab638f..704a8bd3a57 100644
--- a/app/assets/javascripts/shortcuts_blob.js
+++ b/app/assets/javascripts/shortcuts_blob.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js
index f7492a2aa5c..befe4eccdba 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 92ce31969e3..90ed4267661 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 235bf4f95ec..25ec7dbc067 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require mousetrap */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index b04159420d1..19c6b7d30ab 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js
index fb2b39e757e..002e979a2c6 100644
--- a/app/assets/javascripts/shortcuts_network.js
+++ b/app/assets/javascripts/shortcuts_network.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6
index 755fac8107b..a23ca449c4b 100644
--- a/app/assets/javascripts/sidebar.js.es6
+++ b/app/assets/javascripts/sidebar.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
let singleton;
@@ -28,7 +29,7 @@
}
init() {
- this.isPinned = $.cookie(pinnedStateCookie) === 'true';
+ this.isPinned = Cookies.get(pinnedStateCookie) === 'true';
this.isExpanded = (
window.innerWidth >= sidebarBreakpoint &&
$(pageSelector).hasClass(expandedPageClass)
@@ -37,7 +38,8 @@
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body', (e) => this.handleClickEvent(e))
- .on('page:change', () => this.renderState());
+ .on('page:change', () => this.renderState())
+ .on('todo:toggle', (e, count) => this.updateTodoCount(count));
this.renderState();
}
@@ -52,6 +54,10 @@
}
}
+ updateTodoCount(count) {
+ $('.js-todos-count').text(gl.text.addDelimiter(count));
+ }
+
toggleSidebar() {
this.isExpanded = !this.isExpanded;
this.renderState();
@@ -62,10 +68,7 @@
if (!this.isPinned) {
this.isExpanded = false;
}
- $.cookie(pinnedStateCookie, this.isPinned ? 'true' : 'false', {
- path: gon.relative_url_root || '/',
- expires: 3650
- });
+ Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { expires: 3650 });
this.renderState();
}
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index ee6af123268..8e54ca4f0dc 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -12,7 +13,7 @@
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
- function SingleFileDiff(file) {
+ function SingleFileDiff(file, forceLoad, cb) {
this.file = file;
this.toggleDiff = bind(this.toggleDiff, this);
this.content = $('.diff-content', this.file);
@@ -31,9 +32,12 @@
this.$toggleIcon.addClass('fa-caret-down');
}
$('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff);
+ if (forceLoad) {
+ this.toggleDiff(null, cb);
+ }
}
- SingleFileDiff.prototype.toggleDiff = function(e) {
+ SingleFileDiff.prototype.toggleDiff = function(e, cb) {
var $target = $(e.target);
if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
this.isOpen = !this.isOpen;
@@ -53,11 +57,11 @@
}
} else {
this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
- return this.getContentHTML();
+ return this.getContentHTML(cb);
}
};
- SingleFileDiff.prototype.getContentHTML = function() {
+ SingleFileDiff.prototype.getContentHTML = function(cb) {
this.collapsedContent.hide();
this.loadingContent.show();
$.get(this.diffForPath, (function(_this) {
@@ -75,6 +79,8 @@
if (typeof DiffNotesApp !== 'undefined') {
DiffNotesApp.compileComponents();
}
+
+ if (cb) cb();
};
})(this));
};
@@ -83,10 +89,10 @@
})();
- $.fn.singleFileDiff = function() {
+ $.fn.singleFileDiff = function(forceLoad, cb) {
return this.each(function() {
- if (!$.data(this, 'singleFileDiff')) {
- return $.data(this, 'singleFileDiff', new SingleFileDiff(this));
+ if (!$.data(this, 'singleFileDiff') || forceLoad) {
+ return $.data(this, 'singleFileDiff', new SingleFileDiff(this, forceLoad, cb));
}
});
};
diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js
index 855e97eb301..083dc23c796 100644
--- a/app/assets/javascripts/snippet/snippet_bundle.js
+++ b/app/assets/javascripts/snippet/snippet_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/snippets_list.js.es6 b/app/assets/javascripts/snippets_list.js.es6
index 6f0996c0d2a..c3afc3f2246 100644
--- a/app/assets/javascripts/snippets_list.js.es6
+++ b/app/assets/javascripts/snippets_list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index 10509313c12..cfd1e2204d5 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.Star = (function() {
function Star() {
@@ -10,11 +11,9 @@
$this.parent().find('.star-count').text(data.star_count);
if (isStarred) {
$starSpan.removeClass('starred').text('Star');
- gl.utils.updateTooltipTitle($this, 'Star project');
$starIcon.removeClass('fa-star').addClass('fa-star-o');
} else {
$starSpan.addClass('starred').text('Unstar');
- gl.utils.updateTooltipTitle($this, 'Unstar project');
$starIcon.removeClass('fa-star-o').addClass('fa-star');
}
};
diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js
index 5e3c5983d75..f9915593657 100644
--- a/app/assets/javascripts/subscription.js
+++ b/app/assets/javascripts/subscription.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -5,10 +6,10 @@
function Subscription(container) {
this.toggleSubscription = bind(this.toggleSubscription, this);
var $container;
- $container = $(container);
- this.url = $container.attr('data-url');
- this.subscribe_button = $container.find('.js-subscribe-button');
- this.subscription_status = $container.find('.subscription-status');
+ this.$container = $(container);
+ this.url = this.$container.attr('data-url');
+ this.subscribe_button = this.$container.find('.js-subscribe-button');
+ this.subscription_status = this.$container.find('.subscription-status');
this.subscribe_button.unbind('click').click(this.toggleSubscription);
}
@@ -18,17 +19,27 @@
action = btn.find('span').text();
current_status = this.subscription_status.attr('data-status');
btn.addClass('disabled');
+
+ if ($('html').hasClass('issue-boards-page')) {
+ this.url = this.$container.attr('data-url');
+ }
+
return $.post(this.url, (function(_this) {
return function() {
var status;
btn.removeClass('disabled');
- status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed';
- _this.subscription_status.attr('data-status', status);
- action = status === 'subscribed' ? 'Unsubscribe' : 'Subscribe';
- btn.find('span').text(action);
- _this.subscription_status.find('>div').toggleClass('hidden');
- if (btn.attr('data-original-title')) {
- return btn.tooltip('hide').attr('data-original-title', action).tooltip('fixTitle');
+
+ if ($('html').hasClass('issue-boards-page')) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'subscribed', !gl.issueBoards.BoardsStore.detail.issue.subscribed);
+ } else {
+ status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed';
+ _this.subscription_status.attr('data-status', status);
+ action = status === 'subscribed' ? 'Unsubscribe' : 'Subscribe';
+ btn.find('span').text(action);
+ _this.subscription_status.find('>div').toggleClass('hidden');
+ if (btn.attr('data-original-title')) {
+ return btn.tooltip('hide').attr('data-original-title', action).tooltip('fixTitle');
+ }
}
};
})(this));
diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js
index d6c219603d1..2ca65cb762d 100644
--- a/app/assets/javascripts/subscription_select.js
+++ b/app/assets/javascripts/subscription_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.SubscriptionSelect = (function() {
function SubscriptionSelect() {
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index 2ae7bf5fc15..77ad4f30b7a 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Syntax Highlighter
//
// Applies a syntax highlighting color scheme CSS class to any element with the
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js.es6 b/app/assets/javascripts/templates/issuable_template_selector.js.es6
index bd4e3c3d00d..93a3d67ee9f 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selector.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require ../blob/template_selector */
((global) => {
@@ -32,24 +33,22 @@
this.currentTemplate = currentTemplate;
if (err) return; // Error handled by global AJAX error handler
this.stopLoadingSpinner();
- this.setInputValueToTemplateContent(true);
+ this.setInputValueToTemplateContent();
});
return;
}
- setInputValueToTemplateContent(append) {
+ setInputValueToTemplateContent() {
// `this.requestFileSuccess` sets the value of the description input field
- // to the content of the template selected. If `append` is true, the
- // template content will be appended to the previous value of the field,
- // separated by a blank line if the previous value is non-empty.
+ // to the content of the template selected.
if (this.titleInput.val() === '') {
// If the title has not yet been set, focus the title input and
// skip focusing the description input by setting `true` as the
// `skipFocus` option to `requestFileSuccess`.
- this.requestFileSuccess(this.currentTemplate, {skipFocus: true, append});
+ this.requestFileSuccess(this.currentTemplate, {skipFocus: true});
this.titleInput.focus();
} else {
- this.requestFileSuccess(this.currentTemplate, {skipFocus: false, append});
+ this.requestFileSuccess(this.currentTemplate, {skipFocus: false});
}
return;
}
diff --git a/app/assets/javascripts/templates/issuable_template_selectors.js.es6 b/app/assets/javascripts/templates/issuable_template_selectors.js.es6
index 4e8247b89e1..0a3890e85fe 100644
--- a/app/assets/javascripts/templates/issuable_template_selectors.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selectors.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class IssuableTemplateSelectors {
constructor({ $dropdowns, editor } = {}) {
diff --git a/app/assets/javascripts/todos.js.es6 b/app/assets/javascripts/todos.js.es6
index 055228c5df8..213e80825b7 100644
--- a/app/assets/javascripts/todos.js.es6
+++ b/app/assets/javascripts/todos.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
class Todos {
@@ -97,7 +98,8 @@
}
updateBadges(data) {
- $('.todos-pending .badge, .todos-pending-count').text(data.count);
+ $(document).trigger('todo:toggle', data.count);
+ $('.todos-pending .badge').text(data.count);
return $('.todos-done .badge').text(data.done_count);
}
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index 9b7be17c4fe..70aff4b9a2f 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.TreeView = (function() {
function TreeView() {
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index ce2930c7fc7..35f2b1e2b25 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js
index bc48c67c4f2..aff605169e4 100644
--- a/app/assets/javascripts/u2f/error.js
+++ b/app/assets/javascripts/u2f/error.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 926912fa988..22fbf9f3a91 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> registered -> POST to server
diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js
index 907e640161a..2eab2d5ae23 100644
--- a/app/assets/javascripts/u2f/util.js
+++ b/app/assets/javascripts/u2f/util.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.U2FUtil = (function() {
function U2FUtil() {}
diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6
index 0f97924d94e..5e869e99fdb 100644
--- a/app/assets/javascripts/user.js.es6
+++ b/app/assets/javascripts/user.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
global.User = class {
constructor({ action }) {
@@ -23,10 +24,7 @@
hideProjectLimitMessage() {
$('.hide-project-limit-message').on('click', e => {
e.preventDefault();
- const path = gon.relative_url_root || '/';
- $.cookie('hide_project_limit_message', 'false', {
- path: path
- });
+ Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
});
}
diff --git a/app/assets/javascripts/user_tabs.js.es6 b/app/assets/javascripts/user_tabs.js.es6
index dfdfa1e7f75..2b310da319c 100644
--- a/app/assets/javascripts/user_tabs.js.es6
+++ b/app/assets/javascripts/user_tabs.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*
UserTabs
diff --git a/app/assets/javascripts/username_validator.js.es6 b/app/assets/javascripts/username_validator.js.es6
index bf4b2e320cd..c4dde575c6e 100644
--- a/app/assets/javascripts/username_validator.js.es6
+++ b/app/assets/javascripts/username_validator.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline';
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
index 3bd4c3c066f..0ec878e7e60 100644
--- a/app/assets/javascripts/users/calendar.js
+++ b/app/assets/javascripts/users/calendar.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js
index d6e4d9f7ad8..22bee0f6187 100644
--- a/app/assets/javascripts/users/users_bundle.js
+++ b/app/assets/javascripts/users/users_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require_tree . */
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 3020b7cc239..3847278e80a 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
slice = [].slice;
@@ -9,7 +10,11 @@
this.usersPath = "/autocomplete/users.json";
this.userPath = "/autocomplete/users/:id.json";
if (currentUser != null) {
- this.currentUser = JSON.parse(currentUser);
+ if (typeof currentUser === 'object') {
+ this.currentUser = currentUser;
+ } else {
+ this.currentUser = JSON.parse(currentUser);
+ }
}
$('.js-user-search').each((function(_this) {
return function(i, dropdown) {
@@ -32,9 +37,30 @@
$value = $block.find('.value');
$collapsedSidebar = $block.find('.sidebar-collapsed-user');
$loading = $block.find('.block-loading').fadeOut();
+
+ var updateIssueBoardsIssue = function () {
+ $loading.fadeIn();
+ gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ .then(function () {
+ $loading.fadeOut();
+ });
+ };
+
$block.on('click', '.js-assign-yourself', function(e) {
e.preventDefault();
- return assignTo(_this.currentUser.id);
+
+ if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({
+ id: _this.currentUser.id,
+ username: _this.currentUser.username,
+ name: _this.currentUser.name,
+ avatar_url: _this.currentUser.avatar_url
+ }));
+
+ updateIssueBoardsIssue();
+ } else {
+ return assignTo(_this.currentUser.id);
+ }
});
assignTo = function(selected) {
var data;
@@ -150,6 +176,7 @@
// display:block overrides the hide-collapse rule
return $value.css('display', '');
},
+ vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(user, $el, e) {
var isIssueIndex, isMRIndex, page, selected;
page = $('body').data('page');
@@ -160,7 +187,7 @@
selectedId = user.id;
return;
}
- if ($('html').hasClass('issue-boards-page')) {
+ if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
selectedId = user.id;
gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = user.id;
gl.issueBoards.BoardsStore.updateFiltersUrl();
@@ -170,6 +197,19 @@
return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ if (user.id) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({
+ id: user.id,
+ username: user.username,
+ name: user.name,
+ avatar_url: user.avatar_url
+ }));
+ } else {
+ Vue.delete(gl.issueBoards.BoardsStore.detail.issue, 'assignee');
+ }
+
+ updateIssueBoardsIssue();
} else {
selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val();
return assignTo(selected);
diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js
index 35401231fbf..ad9b842db3c 100644
--- a/app/assets/javascripts/wikis.js
+++ b/app/assets/javascripts/wikis.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
/*= require latinise */
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 777b32b41c9..fa124e7052d 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Zen Mode (full screen) textarea
//
/*= provides zen_mode:enter */
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 98e301d3799..ce117c3fba5 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -1,11 +1,36 @@
-.avatar {
+@mixin avatar-size($size, $margin-right) {
+ width: $size;
+ height: $size;
+ margin-right: $margin-right;
+}
+
+.avatar-container {
float: left;
- margin-right: 12px;
+ margin-right: 15px;
+ border-radius: $avatar_radius;
+ border: 1px solid rgba(0, 0, 0, .1);
+ &.s16 { @include avatar-size(16px, 6px); }
+ &.s20 { @include avatar-size(20px, 7px); }
+ &.s24 { @include avatar-size(24px, 8px); }
+ &.s26 { @include avatar-size(26px, 8px); }
+ &.s32 { @include avatar-size(32px, 10px); }
+ &.s36 { @include avatar-size(36px, 10px); }
+ &.s40 { @include avatar-size(40px, 10px); }
+ &.s46 { @include avatar-size(46px, 15px); }
+ &.s48 { @include avatar-size(48px, 10px); }
+ &.s60 { @include avatar-size(60px, 12px); }
+ &.s70 { @include avatar-size(70px, 14px); }
+ &.s90 { @include avatar-size(90px, 15px); }
+ &.s110 { @include avatar-size(110px, 15px); }
+ &.s140 { @include avatar-size(140px, 15px); }
+ &.s160 { @include avatar-size(160px, 20px); }
+}
+
+.avatar {
+ @extend .avatar-container;
width: 40px;
height: 40px;
padding: 0;
- border-radius: $avatar_radius;
- border: 1px solid rgba(0, 0, 0, .1);
&.avatar-inline {
float: none;
@@ -20,22 +45,6 @@
border-radius: 0;
border: none;
}
-
- &.s16 { width: 16px; height: 16px; margin-right: 6px; }
- &.s20 { width: 20px; height: 20px; margin-right: 7px; }
- &.s24 { width: 24px; height: 24px; margin-right: 8px; }
- &.s26 { width: 26px; height: 26px; margin-right: 8px; }
- &.s32 { width: 32px; height: 32px; margin-right: 10px; }
- &.s36 { width: 36px; height: 36px; margin-right: 10px; }
- &.s40 { width: 40px; height: 40px; margin-right: 10px; }
- &.s46 { width: 46px; height: 46px; margin-right: 15px; }
- &.s48 { width: 48px; height: 48px; margin-right: 10px; }
- &.s60 { width: 60px; height: 60px; margin-right: 12px; }
- &.s70 { width: 70px; height: 70px; margin-right: 14px; }
- &.s90 { width: 90px; height: 90px; margin-right: 15px; }
- &.s110 { width: 110px; height: 110px; margin-right: 15px; }
- &.s140 { width: 140px; height: 140px; margin-right: 20px; }
- &.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
.identicon {
@@ -54,3 +63,17 @@
&.s140 { font-size: 72px; line-height: 138px; }
&.s160 { font-size: 96px; line-height: 158px; }
}
+
+.image-container {
+ @extend .avatar-container;
+ overflow: hidden;
+ display: flex;
+
+ .avatar {
+ border-radius: 0;
+ border: none;
+ height: auto;
+ margin: 0;
+ align-self: center;
+ }
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index c0e9c8bf829..ed21ad83a1c 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -216,7 +216,7 @@
svg,
.fa {
&:not(:last-child) {
- margin-right: 3px;
+ margin-right: 5px;
}
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 1de246600fd..baa38ab60c8 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -15,6 +15,7 @@
@media (max-width: $screen-xs-max) {
width: 100%;
+ min-width: 240px;
}
}
@@ -485,7 +486,7 @@
font-size: 20px;
text-indent: 0;
- &:before {
+ &::before {
display: block;
position: relative;
top: -2px;
@@ -517,7 +518,7 @@
background-color: transparent;
border: 0;
- .ui-icon:before {
+ .ui-icon::before {
color: $md-link-color;
}
}
@@ -526,7 +527,7 @@
.ui-datepicker-prev {
left: 0;
- .ui-icon:before {
+ .ui-icon::before {
content: '\f104';
text-align: left;
}
@@ -535,7 +536,7 @@
.ui-datepicker-next {
right: 0;
- .ui-icon:before {
+ .ui-icon::before {
content: '\f105';
text-align: right;
}
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 3f877d86a26..91ab1503439 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -21,57 +21,66 @@
background: $color-darker;
}
- .nav-sidebar li {
- a {
- color: $color-light;
-
- &:hover,
- &:focus,
- &:active {
- background: $color-dark;
- }
+ .sidebar-header,
+ .sidebar-action-buttons {
+ color: $color-light;
+ background-color: lighten($color-darker, 5%);
+ }
- i {
+ .nav-sidebar {
+ li {
+ a {
color: $color-light;
- }
-
- path,
- polygon {
- fill: $color-light;
- }
- .count {
- color: $color-light;
- background: $color-dark;
+ &: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;
+ }
}
- svg {
- position: relative;
- top: 3px;
+ &.separate-item {
+ border-top: 1px solid $color;
}
- }
-
- &.separate-item {
- border-top: 1px solid $color;
- }
- &.active a {
- color: $white-light;
- background: $color-dark;
+ &.active a {
+ color: $white-light;
+ background: $color-dark;
- &.no-highlight {
- border: none;
- }
+ &.no-highlight {
+ border: none;
+ }
- i {
- color: $white-light;
- }
+ i {
+ color: $white-light;
+ }
- path,
- polygon {
- fill: $white-light;
+ path,
+ polygon {
+ fill: $white-light;
+ }
}
}
+
}
}
}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 142076f65b2..4993ca7572a 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -49,12 +49,16 @@ header {
font-size: 18px;
padding: 0;
margin: ($header-height - 28) / 2 0;
- margin-left: 10px;
+ margin-left: 8px;
height: 28px;
min-width: 28px;
line-height: 28px;
text-align: center;
+ &.header-user-dropdown-toggle {
+ margin-left: 14px;
+ }
+
&:hover,
&:focus,
&:active {
@@ -227,7 +231,7 @@ header {
float: none !important;
.visible-xs,
- .visable-sm {
+ .visible-sm {
display: table-cell !important;
}
}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 48e34a0066e..bc0610cc417 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -14,7 +14,7 @@
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
- &:after {
+ &::after {
content: " ";
display: table;
clear: both;
@@ -38,7 +38,7 @@
&.smoke { background-color: $background-color; }
- &:hover {
+ &:not(.ui-sort-disabled):hover {
background: $row-hover;
}
@@ -142,10 +142,6 @@ ul.content-list {
}
}
- .avatar {
- margin-right: 15px;
- }
-
.controls {
float: right;
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index b6f21fd8c91..cb2c351c368 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -7,8 +7,70 @@
.pagination {
padding: 0;
}
+
+ .gap,
+ .gap:hover {
+ background-color: $gray-light;
+ padding: $gl-vert-padding;
+ cursor: default;
+ }
}
.panel > .gl-pagination {
margin: 0;
}
+
+/**
+ * Extra-small screen pagination.
+ */
+@media (max-width: 320px) {
+ .gl-pagination {
+ .first,
+ .last {
+ display: none;
+ }
+
+ .page {
+ display: none;
+
+ &.active {
+ display: inline;
+ }
+ }
+ }
+}
+
+/**
+ * Small screen pagination
+ */
+@media (max-width: $screen-xs) {
+ .gl-pagination {
+ .pagination li a {
+ padding: 6px 10px;
+ }
+
+ .page {
+ display: none;
+
+ &.active {
+ display: inline;
+ }
+ }
+ }
+}
+
+/**
+ * Medium screen pagination
+ */
+@media (min-width: $screen-xs) and (max-width: $screen-md-max) {
+ .gl-pagination {
+ .page {
+ display: none;
+
+ &.active,
+ &.sibling {
+ display: inline;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 1d8e64a0e4b..d74c14ee2a4 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -59,6 +59,11 @@
padding: 0 !important;
}
+ .sidebar-header {
+ padding: 11px 22px 12px;
+ font-size: 20px;
+ }
+
li {
&.separate-item {
padding-top: 10px;
@@ -164,6 +169,18 @@
padding-left: $sidebar_width;
}
}
+
+ .merge-request-tabs-holder.affix {
+ @media (min-width: $sidebar-breakpoint) {
+ left: $sidebar_width;
+ }
+ }
+
+ &.right-sidebar-expanded {
+ .line-resolve-all-container {
+ display: none;
+ }
+ }
}
header.header-sidebar-pinned {
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index eb63a9f214b..875cded8b4e 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -45,7 +45,7 @@
@media (max-width: $screen-xs-max) {
.timeline {
- &:before {
+ &::before {
background: none;
}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 266a8024809..070e42d63d2 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -148,7 +148,7 @@
a[href*="/uploads/"],
a[href*="storage.googleapis.com/google-code-attachments/"] {
- &:before {
+ &::before {
margin-right: 4px;
font: normal normal normal 14px/1 FontAwesome;
@@ -158,13 +158,13 @@
content: "\f0c6";
}
- &:hover:before {
+ &:hover::before {
text-decoration: none;
}
}
a.no-attachment-icon {
- &:before {
+ &::before {
display: none;
}
}
@@ -183,13 +183,13 @@
position: absolute;
text-decoration: none;
- &:after {
+ &::after {
content: image-url('icon_anchor.svg');
visibility: hidden;
}
}
- &:hover > a.anchor:after {
+ &:hover > a.anchor::after {
visibility: visible;
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index b271f8cf332..be2a7ceefff 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -90,6 +90,8 @@ $table-border-color: #f0f0f0;
$background-color: $gray-light;
$dark-background-color: #f5f5f5;
$table-text-gray: #8f8f8f;
+$widget-expand-item: #e8f2f7;
+$widget-inner-border: #eef0f2;
/*
* Text
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index 63396a6bb29..6cefafd8fc7 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -80,10 +80,13 @@
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
+ white-space: nowrap;
}
.user-details {
flex: 1 1 auto;
+ overflow: hidden;
+ padding-right: 8px;
}
.user-name {
@@ -91,6 +94,12 @@
font-weight: 600;
}
+ .user-name,
+ .user-email {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
.dropdown {
.btn-block {
margin-bottom: 0;
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index 9282e0ae03b..486ad16ea26 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -1,7 +1,7 @@
.awards {
.emoji-icon {
- width: 20px;
- height: 20px;
+ width: 19px;
+ height: 19px;
}
}
@@ -94,7 +94,7 @@
.award-control {
margin: 3px 5px 3px 0;
- padding: 6px 5px;
+ padding: 5px 6px;
outline: 0;
&:hover,
@@ -127,7 +127,7 @@
.award-control-icon {
float: left;
margin-right: 5px;
- font-size: 20px;
+ font-size: 19px;
}
.award-control-icon-loading {
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index d8fabbdcebe..ef6833c9845 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -45,6 +45,15 @@
.page-with-sidebar {
padding-bottom: 0;
}
+
+ .issues-filters {
+ position: relative;
+ z-index: 999999;
+ }
+}
+
+.boards-app {
+ position: relative;
}
.boards-app-loading {
@@ -66,6 +75,10 @@
height: 475px; // Needed for PhantomJS
height: calc(100vh - 220px);
min-height: 475px;
+
+ &.is-compact {
+ width: calc(100% - 290px);
+ }
}
}
@@ -184,6 +197,10 @@
margin-bottom: 5px;
}
+ &.is-active {
+ background-color: $row-hover;
+ }
+
.label {
border: 0;
outline: 0;
@@ -212,6 +229,10 @@
margin-right: 5px;
font-size: (14px / $issue-boards-font-size) * 1em;
}
+
+ .avatar {
+ margin-left: 0;
+ }
}
.card-number {
@@ -264,3 +285,48 @@
border-width: 1px 0 1px 1px;
}
}
+
+.issue-boards-sidebar {
+ &.right-sidebar {
+ top: 153px;
+ bottom: 0;
+
+ @media (min-width: $screen-sm-min) {
+ top: 220px;
+ }
+ }
+
+ .issuable-sidebar-header {
+ position: relative;
+ }
+
+ .gutter-toggle {
+ position: absolute;
+ top: 0;
+ bottom: 15px;
+ right: 0;
+ width: 22px;
+ color: $gray-darkest;
+
+ svg {
+ position: absolute;
+ top: 50%;
+ margin-top: (-11px / 2);
+ }
+
+ &:hover {
+ path {
+ fill: $gray-darkest;
+ }
+ }
+ }
+
+ .issuable-header-text {
+ width: 100%;
+ padding-right: 35px;
+
+ > strong {
+ font-weight: 600;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index d6a55fbd464..6300ac9662f 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -52,10 +52,25 @@
.build-header {
position: relative;
- padding-right: 40px;
+ padding: 0;
+ display: flex;
+ min-height: 58px;
+ align-items: center;
- @media (min-width: $screen-sm-min) {
- padding-right: 0;
+ .btn-inverted {
+ @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
+ }
+
+ @media (max-width: $screen-sm-max) {
+ padding-right: 40px;
+
+ .btn-inverted {
+ display: none;
+ }
+ }
+
+ .header-content {
+ flex: 1;
}
a {
@@ -137,10 +152,15 @@
.retry-link {
color: $gl-link-color;
+ display: none;
&:hover {
text-decoration: underline;
}
+
+ @media (max-width: $screen-sm-max) {
+ display: block;
+ }
}
.stage-item {
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 8ecac08137b..8ecf7fcb96d 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -33,10 +33,8 @@
&.commit-info-row-header {
line-height: 34px;
-
- @media (min-width: $screen-sm-min) {
- margin-bottom: 0;
- }
+ padding: 10px 0;
+ margin-bottom: 0;
.commit-options-dropdown-caret {
@media (max-width: $screen-sm) {
@@ -80,6 +78,58 @@
}
}
+.js-details-expand {
+ &:hover {
+ text-decoration: none;
+ }
+}
+
+.commit-info-widget {
+ background: $background-color;
+ color: $gl-gray;
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+
+ .widget-row {
+ padding: $gl-padding;
+
+ &:not(:last-of-type) {
+ border-bottom: 1px solid $widget-inner-border;
+ }
+
+ &.branch-info {
+ .monospace,
+ .commit-info {
+ margin-left: 4px;
+ }
+ }
+ }
+
+ .icon-container {
+ display: inline-block;
+ margin-right: 8px;
+
+ svg {
+ position: relative;
+ top: 2px;
+ height: 16px;
+ width: 16px;
+ }
+
+ &.commit-icon {
+ svg {
+ path {
+ fill: $gl-text-color;
+ }
+ }
+ }
+ }
+
+ .label.label-gray {
+ background-color: $widget-expand-item;
+ }
+}
+
.ci-status-link {
svg {
overflow: visible;
@@ -88,6 +138,7 @@
.commit-box {
border-top: 1px solid $border-color;
+ padding: $gl-padding 0;
.commit-title {
margin: 0;
@@ -138,6 +189,9 @@
}
.commit-action-buttons {
+ position: relative;
+ top: -1px;
+
i {
color: $gl-icon-color;
font-size: 13px;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index ad315cfae62..52d6a39bd59 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -33,21 +33,22 @@
color: $gl-dark-link-color;
}
- .text-expander {
- display: inline-block;
- background: $gray-light;
- color: $gl-placeholder-color;
- padding: 0 5px;
- cursor: pointer;
- border: 1px solid $border-gray-dark;
- border-radius: $border-radius-default;
- margin-left: 5px;
- line-height: 1;
-
- &:hover {
- background-color: darken($gray-light, 10%);
- text-decoration: none;
- }
+}
+
+.text-expander {
+ display: inline-block;
+ background: $gray-light;
+ color: $gl-placeholder-color;
+ padding: 0 5px;
+ cursor: pointer;
+ border: 1px solid $border-gray-dark;
+ border-radius: $border-radius-default;
+ margin-left: 5px;
+ line-height: 1;
+
+ &:hover {
+ background-color: darken($gray-light, 10%);
+ text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index 76225ed8d06..016bab104eb 100644
--- a/app/assets/stylesheets/pages/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -36,10 +36,6 @@
}
}
-.dash-project-avatar {
- float: left;
-}
-
.dash-project-access-icon {
float: left;
margin-right: 5px;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index e0367d1d942..fde138c874d 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -94,14 +94,14 @@
position: relative;
&.old {
- &:before {
+ &::before {
content: '-';
position: absolute;
}
}
&.new {
- &:before {
+ &::before {
content: '+';
position: absolute;
}
@@ -471,7 +471,7 @@
.file-holder {
.diff-line-num:not(.js-unfold-bottom) {
a {
- &:before {
+ &::before {
content: attr(data-linenumber);
}
}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index ee2a398f031..4375e29c8db 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -3,12 +3,14 @@
}
.dashboard .side .panel .panel-heading .input-group {
+
.form-control {
height: 42px;
}
}
.group-row {
+
.stats {
float: right;
line-height: $list-text-height;
@@ -21,12 +23,14 @@
}
.ldap-group-links {
+
.form-actions {
margin-bottom: $gl-padding;
}
}
.groups-cover-block {
+
.container-fluid {
position: relative;
}
@@ -41,9 +45,14 @@
background-color: $background-color;
}
}
+
+ .group-avatar {
+ border: 0;
+ }
}
.groups-header {
+
@media (min-width: $screen-sm-min) {
.nav-links {
width: 35%;
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 3d2b024fe5c..a2f5c6c6bd3 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -54,7 +54,6 @@
margin: 0 0 10px;
}
-
.login-footer {
margin-top: 10px;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 70afa568554..f8e31a624ec 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -60,7 +60,7 @@
}
.ci_widget {
- border-bottom: 1px solid #eef0f2;
+ border-bottom: 1px solid $widget-inner-border;
svg {
margin-right: 4px;
@@ -279,6 +279,10 @@
#modal_merge_info .modal-dialog {
width: 600px;
+ .dark {
+ margin-right: 40px;
+ }
+
.btn-clipboard {
@extend .pull-right;
@@ -439,12 +443,12 @@
}
.merge-request-tabs-holder {
- background-color: #fff;
+ background-color: $white-light;
&.affix {
top: 100px;
left: 0;
- z-index: 9;
+ z-index: 10;
transition: right .15s;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index b90c91831f2..526e9ae5cdd 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -105,11 +105,6 @@ ul.notes {
padding: 2px;
margin-top: 10px;
}
-
- .award-control {
- font-size: 13px;
- padding: 2px 5px;
- }
}
.note-header {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index f88175365c6..a8e8bbcb208 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -474,8 +474,8 @@
}
.arrow {
- &:before,
- &:after {
+ &::before,
+ &::after {
content: '';
display: inline-block;
position: absolute;
@@ -486,14 +486,14 @@
top: 18px;
}
- &:before {
+ &::before {
left: -5px;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: $border-color;
}
- &:after {
+ &::after {
left: -4px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
@@ -573,8 +573,7 @@
.build {
// Remove right connecting horizontal line from first build in last stage
&:first-child {
- &::after,
- &::before {
+ &::after {
border: none;
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 3f6fdaebc1d..ede29db1979 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -77,14 +77,14 @@
// Middle dot divider between each element in a list of items.
.middle-dot-divider {
- &:after {
+ &::after {
content: "\00B7"; // Middle Dot
padding: 0 6px;
font-weight: bold;
}
&:last-child {
- &:after {
+ &::after {
content: "";
padding: 0;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index f6355941837..f7d54564530 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -96,8 +96,8 @@
.project-avatar {
float: none;
- margin-left: auto;
- margin-right: auto;
+ margin: 0 auto;
+ border: none;
&.identicon {
border-radius: 50%;
@@ -193,7 +193,7 @@
margin-left: 4px;
.arrow {
- &:before {
+ &::before {
content: '';
display: inline-block;
position: absolute;
@@ -209,7 +209,7 @@
pointer-events: none;
}
- &:after {
+ &::after {
content: '';
position: absolute;
width: 0;
@@ -351,7 +351,7 @@ a.deploy-project-label {
line-height: 36px;
margin: 0;
- > li + li:before {
+ > li + li::before {
padding: 0 3px;
color: #999;
}
@@ -790,7 +790,7 @@ pre.light-well {
top: 7px;
color: $location-icon-color;
- &:before {
+ &::before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 6d472e8293f..bf688af50e2 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -72,7 +72,7 @@
top: 0;
color: $location-icon-color;
- &:before {
+ &::before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index ea76fe18876..b3aef2fdd32 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -161,3 +161,63 @@
}
}
}
+
+.todos-empty {
+ display: -webkit-flex;
+ display: flex;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ max-width: 900px;
+ margin-left: auto;
+ margin-right: auto;
+
+ @media (min-width: $screen-sm-min) {
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ padding-top: 80px;
+ }
+}
+
+.todos-empty-content {
+ -webkit-align-self: center;
+ align-self: center;
+ max-width: 480px;
+ margin-right: 20px;
+}
+
+.todos-empty-hero {
+ width: 200px;
+ margin-left: auto;
+ margin-right: auto;
+
+ @media (min-width: $screen-sm-min) {
+ width: 300px;
+ margin-right: 0;
+ -webkit-order: 2;
+ order: 2;
+ }
+}
+
+.todos-all-done {
+ padding-top: 20px;
+
+ @media (min-width: $screen-sm-min) {
+ padding-top: 50px;
+ }
+
+ > svg {
+ display: block;
+ max-width: 300px;
+ margin: 0 auto 20px;
+ }
+
+ p {
+ max-width: 470px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ a {
+ font-weight: 600;
+ }
+}
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
index 587bd6a1e8a..e73cecc92be 100644
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -5,7 +5,7 @@
}
.example {
- &:before {
+ &::before {
content: "Example";
color: #bbb;
}
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index 8239b7e6879..0ff3c3f5472 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -35,7 +35,7 @@ nav.navbar-collapse.collapse,
.nav,
.btn,
ul.notes-form,
-.merge-request-ci-status .ci-status-link:after,
+.merge-request-ci-status .ci-status-link::after,
.issuable-gutter-toggle,
.gutter-toggle,
.issuable-details .content-block-small,
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index f35f4a8c811..bb912ed10cc 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -3,7 +3,7 @@ class Admin::UsersController < Admin::ApplicationController
def index
@users = User.order_name_asc.filter(params[:filter])
- @users = @users.search(params[:name]) if params[:name].present?
+ @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
@users = @users.sort(@sort = params[:sort])
@users = @users.page(params[:page])
end
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index 4cb3be41064..c33d7eecb9f 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -18,7 +18,7 @@ module ServiceParams
:add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
- :jira_issue_transition_id]
+ :jira_issue_transition_id, :url, :project_key]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
diff --git a/app/controllers/projects/boards/issues_controller.rb b/app/controllers/projects/boards/issues_controller.rb
index a2b01ff43dc..dc33e1405f2 100644
--- a/app/controllers/projects/boards/issues_controller.rb
+++ b/app/controllers/projects/boards/issues_controller.rb
@@ -73,10 +73,13 @@ module Projects
def serialize_as_json(resource)
resource.as_json(
labels: true,
- only: [:iid, :title, :confidential],
+ only: [:iid, :title, :confidential, :due_date],
include: {
- assignee: { only: [:id, :name, :username], methods: [:avatar_url] }
- })
+ assignee: { only: [:id, :name, :username], methods: [:avatar_url] },
+ milestone: { only: [:id, :title] }
+ },
+ user: current_user
+ )
end
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index cb649264146..3f1a1d1c511 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -112,7 +112,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
format.json do
- render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
+ render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end
end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 4f855134368..42fd09e9b7e 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -126,7 +126,7 @@ class Projects::LabelsController < Projects::ApplicationController
alias_method :subscribable_resource, :label
def find_labels
- @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute.includes(:priorities)
+ @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
end
def authorize_admin_labels!
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 2ee53f7ceda..30f1cf4e5be 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -278,7 +278,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.target_project, @merge_request])
end
format.json do
- render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
+ render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end
end
else
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index d08f490de18..699a56ae2f8 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -25,18 +25,15 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
def create
- if params[:user_ids].blank?
- return redirect_to(namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.')
- end
+ status = Members::CreateService.new(@project, current_user, params).execute
- @project.team.add_users(
- params[:user_ids].split(','),
- params[:access_level],
- expires_at: params[:expires_at],
- current_user: current_user
- )
+ redirect_url = namespace_project_project_members_path(@project.namespace, @project)
- redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.'
+ if status
+ redirect_to redirect_url, notice: 'Users were successfully added.'
+ else
+ redirect_to redirect_url, alert: 'No users or groups specified.'
+ end
end
def update
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 8fea20cefef..953091492ae 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -23,7 +23,7 @@ class Projects::TagsController < Projects::ApplicationController
return render_404 unless @tag
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
- @commit = @repository.commit(@tag.target)
+ @commit = @repository.commit(@tag.dereferenced_target)
end
def create
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 76b730198d4..bce5e29d8d8 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -30,6 +30,8 @@ class ProjectsController < Projects::ApplicationController
@project = ::Projects::CreateService.new(current_user, project_params).execute
if @project.saved?
+ cookies[:issue_board_welcome_hidden] = { path: project_path(@project), value: nil, expires: Time.at(0) }
+
redirect_to(
project_path(@project),
notice: "Project '#{@project.name}' was successfully created."
@@ -287,7 +289,8 @@ class ProjectsController < Projects::ApplicationController
render 'projects/empty' if @project.empty_repo?
else
if @project.wiki_enabled?
- @wiki_home = @project.wiki.find_page('home', params[:version_id])
+ @project_wiki = @project.wiki
+ @wiki_home = @project_wiki.find_page('home', params[:version_id])
elsif @project.feature_available?(:issues, current_user)
@issues = issues_collection
@issues = @issues.page(params[:page])
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index e27986ef95b..cc2073081b5 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -126,7 +126,7 @@ class IssuableFinder
@labels =
if labels? && !filter_by_no_label?
- LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute
+ LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true)
else
Label.none
end
@@ -273,7 +273,7 @@ class IssuableFinder
items = items.with_label(label_names, params[:sort])
if projects
- label_ids = LabelsFinder.new(current_user, project_ids: projects).execute.select(:id)
+ label_ids = LabelsFinder.new(current_user, project_ids: projects).execute(skip_authorization: true).select(:id)
items = items.where(labels: { id: label_ids })
end
end
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 95e62cdb02a..865f093f04a 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -4,9 +4,8 @@ class LabelsFinder < UnionFinder
@params = params
end
- def execute(authorized_only: true)
- @authorized_only = authorized_only
-
+ def execute(skip_authorization: false)
+ @skip_authorization = skip_authorization
items = find_union(label_ids, Label)
items = with_title(items)
sort(items)
@@ -14,7 +13,7 @@ class LabelsFinder < UnionFinder
private
- attr_reader :current_user, :params, :authorized_only
+ attr_reader :current_user, :params, :skip_authorization
def label_ids
label_ids = []
@@ -50,7 +49,7 @@ class LabelsFinder < UnionFinder
end
def projects_ids
- params[:project_ids].presence
+ params[:project_ids]
end
def title
@@ -70,17 +69,17 @@ class LabelsFinder < UnionFinder
end
def find_project
- if authorized_only
- available_projects.find_by(id: project_id)
- else
+ if skip_authorization
Project.find_by(id: project_id)
+ else
+ available_projects.find_by(id: project_id)
end
end
def projects
return @projects if defined?(@projects)
- @projects = authorized_only ? available_projects : Project.all
+ @projects = skip_authorization ? Project.all : available_projects
@projects = @projects.in_namespace(group_id) if group_id
@projects = @projects.where(id: projects_ids) if projects_ids
@projects = @projects.reorder(nil)
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 85e1dc33ee8..dee3c78df47 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -16,13 +16,14 @@ module ButtonHelper
# See http://clipboardjs.com/#usage
def clipboard_button(data = {})
css_class = data[:class] || 'btn-clipboard btn-transparent'
+ title = data[:title] || 'Copy to Clipboard'
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
content_tag :button,
icon('clipboard'),
class: "btn #{css_class}",
data: data,
type: :button,
- title: 'Copy to Clipboard'
+ title: title
end
def http_clone_button(project, placement = 'right', append_link: true)
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index f8ded05c31a..00e64076408 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -39,6 +39,12 @@ module EventsHelper
end
end
+ def event_filter_visible(feature_key)
+ return true unless @project
+
+ @project.feature_available?(feature_key, current_user)
+ end
+
def event_preposition(event)
if event.push? || event.commented? || event.target
"at"
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 03b2db1bc91..ef6cfb235a9 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -71,6 +71,14 @@ module IssuablesHelper
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end
+
+ if issuable.tasks?
+ output << "&ensp;".html_safe
+ output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs")
+ output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-sm hidden-md hidden-lg")
+ end
+
+ output
end
def issuable_todo(issuable)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index d26b4018be6..42c00ec3cd5 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -174,10 +174,14 @@ module ProjectsHelper
nav_tabs << :merge_requests
end
- if can?(current_user, :read_build, project)
+ if can?(current_user, :read_pipeline, project)
nav_tabs << :pipelines
end
+ if can?(current_user, :read_build, project)
+ nav_tabs << :builds
+ end
+
if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
nav_tabs << :container_registry
end
diff --git a/app/helpers/sidekiq_helper.rb b/app/helpers/sidekiq_helper.rb
index d440edc55ba..56749d80bd3 100644
--- a/app/helpers/sidekiq_helper.rb
+++ b/app/helpers/sidekiq_helper.rb
@@ -5,7 +5,7 @@ module SidekiqHelper
(?<mem>[\d\.,]+)\s+
(?<state>[DRSTWXZNLsl\+<]+)\s+
(?<start>.+)\s+
- (?<command>sidekiq.*\])\s+
+ (?<command>sidekiq.*\])\s*
\z/x
def parse_sidekiq_ps(line)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 826539d5c4d..3fee6c18770 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -30,23 +30,23 @@ module Ci
end
event :run do
- transition any => :running
+ transition any - [:running] => :running
end
event :skip do
- transition any => :skipped
+ transition any - [:skipped] => :skipped
end
event :drop do
- transition any => :failed
+ transition any - [:failed] => :failed
end
event :succeed do
- transition any => :success
+ transition any - [:success] => :success
end
event :cancel do
- transition any => :canceled
+ transition any - [:canceled] => :canceled
end
# IMPORTANT
@@ -271,7 +271,7 @@ module Ci
end
def update_status
- with_lock do
+ Gitlab::OptimisticLocking.retry_lock(self) do
case latest_builds_status
when 'pending' then enqueue
when 'running' then run
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 4cb3a69416e..d159fc6c5c7 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -73,16 +73,16 @@ class CommitStatus < ActiveRecord::Base
transition [:created, :pending, :running] => :canceled
end
- after_transition created: [:pending, :running] do |commit_status|
- commit_status.update_attributes queued_at: Time.now
+ before_transition created: [:pending, :running] do |commit_status|
+ commit_status.queued_at = Time.now
end
- after_transition [:created, :pending] => :running do |commit_status|
- commit_status.update_attributes started_at: Time.now
+ before_transition [:created, :pending] => :running do |commit_status|
+ commit_status.started_at = Time.now
end
- after_transition any => [:success, :failed, :canceled] do |commit_status|
- commit_status.update_attributes finished_at: Time.now
+ before_transition any => [:success, :failed, :canceled] do |commit_status|
+ commit_status.finished_at = Time.now
end
after_transition do |commit_status, transition|
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 17c3b526c97..613444e0d70 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -12,6 +12,7 @@ module Issuable
include Subscribable
include StripAttribute
include Awardable
+ include Taskable
included do
cache_markdown_field :title, pipeline: :single_line
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 9216122923e..6d88951c713 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -31,7 +31,7 @@ module ProjectFeaturesCompatibility
def write_feature_attribute(field, value)
build_project_feature unless project_feature
- access_level = value == "true" ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
+ access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
project_feature.update_attribute(field, access_level)
end
end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 12b23f00769..7edb0acd56c 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -38,16 +38,21 @@ module Sortable
private
- def highest_label_priority(target_type:, target_column:, project_column:, excluded_labels: [])
+ def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
query = Label.select(LabelPriority.arel_table[:priority].minimum).
left_join_priorities.
joins(:label_links).
where("label_priorities.project_id = #{project_column}").
- where(label_links: { target_type: target_type }).
where("label_links.target_id = #{target_column}").
reorder(nil)
- query.where.not(title: excluded_labels) if excluded_labels.present?
+ if target_type_column
+ query = query.where("label_links.target_type = #{target_type_column}")
+ else
+ query = query.where(label_links: { target_type: target_type })
+ end
+
+ query = query.where.not(title: excluded_labels) if excluded_labels.present?
query
end
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index a3ac577cf3e..ebc75100a54 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -53,10 +53,22 @@ module Taskable
# Return a string that describes the current state of this Taskable's task
# list items, e.g. "12 of 20 tasks completed"
- def task_status
+ def task_status(short: false)
return '' if description.blank?
+ prep, completed = if short
+ ['/', '']
+ else
+ [' of ', ' completed']
+ end
+
sum = tasks.summary
- "#{sum.complete_count} of #{sum.item_count} #{'task'.pluralize(sum.item_count)} completed"
+ "#{sum.complete_count}#{prep}#{sum.item_count} #{'task'.pluralize(sum.item_count)}#{completed}"
+ end
+
+ # Return a short string that describes the current state of this Taskable's
+ # task list items -- for small screens
+ def task_status_short
+ task_status(short: true)
end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 3993b35f96d..43e67069b70 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -1,6 +1,6 @@
class Event < ActiveRecord::Base
include Sortable
- default_scope { where.not(author_id: nil) }
+ default_scope { reorder(nil).where.not(author_id: nil) }
CREATED = 1
UPDATED = 2
diff --git a/app/models/group.rb b/app/models/group.rb
index 552e1154df6..d9e90cd256a 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -68,7 +68,7 @@ class Group < Namespace
end
def web_url
- Gitlab::Routing.url_helpers.group_url(self)
+ Gitlab::Routing.url_helpers.group_canonical_url(self)
end
def human_name
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
index a698b532d19..68841ace2e6 100644
--- a/app/models/group_label.rb
+++ b/app/models/group_label.rb
@@ -5,6 +5,10 @@ class GroupLabel < Label
alias_attribute :subject, :group
+ def subject_foreign_key
+ 'group_id'
+ end
+
def to_reference(source_project = nil, target_project = nil, format: :id)
super(source_project, target_project, format: format)
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 89158a50353..4f02b02c488 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base
include Issuable
include Referable
include Sortable
- include Taskable
include Spammable
include FasterCacheKeys
@@ -287,10 +286,12 @@ class Issue < ActiveRecord::Base
def as_json(options = {})
super(options).tap do |json|
+ json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user)
+
if options.has_key?(:labels)
json[:labels] = labels.as_json(
project: project,
- only: [:id, :title, :description, :color],
+ only: [:id, :title, :description, :color, :priority],
methods: [:text_color]
)
end
diff --git a/app/models/label.rb b/app/models/label.rb
index 149fd98ecb3..d9287f2dc29 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -92,16 +92,23 @@ class Label < ActiveRecord::Base
nil
end
- def open_issues_count(user = nil, project = nil)
- issues_count(user, project_id: project.try(:id) || project_id, state: 'opened')
+ def open_issues_count(user = nil)
+ issues_count(user, state: 'opened')
end
- def closed_issues_count(user = nil, project = nil)
- issues_count(user, project_id: project.try(:id) || project_id, state: 'closed')
+ def closed_issues_count(user = nil)
+ issues_count(user, state: 'closed')
end
- def open_merge_requests_count(user = nil, project = nil)
- merge_requests_count(user, project_id: project.try(:id) || project_id, state: 'opened')
+ def open_merge_requests_count(user = nil)
+ params = {
+ subject_foreign_key => subject.id,
+ label_name: title,
+ scope: 'all',
+ state: 'opened'
+ }
+
+ MergeRequestsFinder.new(user, params.with_indifferent_access).execute.count
end
def prioritize!(project, value)
@@ -167,15 +174,8 @@ class Label < ActiveRecord::Base
end
def issues_count(user, params = {})
- IssuesFinder.new(user, params.reverse_merge(label_name: title, scope: 'all'))
- .execute
- .count
- end
-
- def merge_requests_count(user, params = {})
- MergeRequestsFinder.new(user, params.reverse_merge(label_name: title, scope: 'all'))
- .execute
- .count
+ params.merge!(subject_foreign_key => subject.id, label_name: title, scope: 'all')
+ IssuesFinder.new(user, params.with_indifferent_access).execute.count
end
def label_format_reference(format = :id)
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 18657c3e1c8..7712d5783e0 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -17,4 +17,10 @@ class LfsObject < ActiveRecord::Base
def project_allowed_access?(project)
projects.exists?(storage_project(project).id)
end
+
+ def self.destroy_unreferenced
+ joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id")
+ .where(lfs_objects_projects: { id: nil })
+ .destroy_all
+ end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 4872f8b8649..0397c57f935 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base
include Issuable
include Referable
include Sortable
- include Taskable
include Importable
belongs_to :target_project, class_name: "Project"
diff --git a/app/models/project.rb b/app/models/project.rb
index fbf7012972e..d5512dfaf9c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -30,6 +30,11 @@ class Project < ActiveRecord::Base
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
default_value_for(:repository_storage) { current_application_settings.repository_storage }
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
+ default_value_for :issues_enabled, gitlab_config_features.issues
+ default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
+ default_value_for :builds_enabled, gitlab_config_features.builds
+ default_value_for :wiki_enabled, gitlab_config_features.wiki
+ default_value_for :snippets_enabled, gitlab_config_features.snippets
after_create :ensure_dir_exist
after_create :create_project_feature, unless: :project_feature
@@ -390,7 +395,7 @@ class Project < ActiveRecord::Base
end
def group_ids
- joins(:namespace).where(namespaces: { type: 'Group' }).pluck(:namespace_id)
+ joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
end
end
@@ -738,7 +743,7 @@ class Project < ActiveRecord::Base
def create_labels
Label.templates.each do |label|
params = label.attributes.except('id', 'template', 'created_at', 'updated_at')
- Labels::FindOrCreateService.new(owner, self, params).execute
+ Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true)
end
end
diff --git a/app/models/project_label.rb b/app/models/project_label.rb
index 33c2b617715..82f47f0e8fd 100644
--- a/app/models/project_label.rb
+++ b/app/models/project_label.rb
@@ -12,6 +12,10 @@ class ProjectLabel < Label
alias_attribute :subject, :project
+ def subject_foreign_key
+ 'project_id'
+ end
+
def to_reference(target_project = nil, format: :id)
super(project, target_project, format: format)
end
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
index 81af55aa29a..338e685339a 100644
--- a/app/models/project_services/bugzilla_service.rb
+++ b/app/models/project_services/bugzilla_service.rb
@@ -1,4 +1,6 @@
class BugzillaService < IssueTrackerService
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index d9fba3d4a41..b2f426dc2ac 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -1,4 +1,6 @@
class CustomIssueTrackerService < IssueTrackerService
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 5d17c358330..6bd8d4ec568 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -1,6 +1,8 @@
class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing.url_helpers
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
default_value_for :default, true
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index b26ddd518d7..207bb816ad1 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -1,6 +1,4 @@
class IssueTrackerService < Service
- validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
-
default_value_for :category, 'issue_tracker'
# Pattern used to extract links from comments
@@ -38,18 +36,24 @@ class IssueTrackerService < Service
]
end
- def initialize_properties
- if properties.nil?
- if enabled_in_gitlab_config
+ # Initialize with default properties values
+ # or receive a block with custom properties
+ def initialize_properties(&block)
+ return unless properties.nil?
+
+ if enabled_in_gitlab_config
+ if block_given?
+ yield
+ else
self.properties = {
title: issues_tracker['title'],
project_url: issues_tracker['project_url'],
issues_url: issues_tracker['issues_url'],
new_issue_url: issues_tracker['new_issue_url']
}
- else
- self.properties = {}
end
+ else
+ self.properties = {}
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index f81b66fd219..5bcf199d468 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -1,15 +1,32 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
+#
+
class JiraService < IssueTrackerService
- include HTTParty
include Gitlab::Routing.url_helpers
- DEFAULT_API_VERSION = 2
-
- prop_accessor :username, :password, :api_url, :jira_issue_transition_id,
- :title, :description, :project_url, :issues_url, :new_issue_url
+ validates :url, url: true, presence: true, if: :activated?
+ validates :project_key, presence: true, if: :activated?
- validates :api_url, presence: true, url: true, if: :activated?
-
- before_validation :set_api_url, :set_jira_issue_transition_id
+ prop_accessor :username, :password, :url, :project_key,
+ :jira_issue_transition_id, :title, :description
before_update :reset_password
@@ -18,16 +35,46 @@ class JiraService < IssueTrackerService
@reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
end
+ def initialize_properties
+ super do
+ self.properties = {
+ title: issues_tracker['title'],
+ url: issues_tracker['url']
+ }
+ end
+ end
+
def reset_password
# don't reset the password if a new one is provided
- if api_url_changed? && !password_touched?
+ if url_changed? && !password_touched?
self.password = nil
end
end
+ def options
+ url = URI.parse(self.url)
+
+ {
+ username: self.username,
+ password: self.password,
+ site: URI.join(url, '/').to_s,
+ context_path: url.path,
+ auth_type: :basic,
+ read_timeout: 120,
+ use_ssl: url.scheme == 'https'
+ }
+ end
+
+ def client
+ @client ||= JIRA::Client.new(options)
+ end
+
+ def jira_project
+ @jira_project ||= client.Project.find(project_key)
+ end
+
def help
- 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
- 'allow a user to easily navigate to the Jira issue tracker. See the '\
+ 'See the ' \
'[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\
'for details.'
end
@@ -53,12 +100,26 @@ class JiraService < IssueTrackerService
end
def fields
- super.push(
- { type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' },
+ [
+ { type: 'text', name: 'url', title: 'URL', placeholder: 'https://jira.example.com' },
+ { type: 'text', name: 'project_key', placeholder: 'Project Key' },
{ type: 'text', name: 'username', placeholder: '' },
{ type: 'password', name: 'password', placeholder: '' },
{ type: 'text', name: 'jira_issue_transition_id', placeholder: '2' }
- )
+ ]
+ end
+
+ # URLs to redirect from Gitlab issues pages to jira issue tracker
+ def project_url
+ "#{url}/issues/?jql=project=#{project_key}"
+ end
+
+ def issues_url
+ "#{url}/browse/:id"
+ end
+
+ def new_issue_url
+ "#{url}/secure/CreateIssue.jspa"
end
def execute(push, issue = nil)
@@ -72,7 +133,7 @@ class JiraService < IssueTrackerService
end
def create_cross_reference_note(mentioned, noteable, author)
- issue_name = mentioned.id
+ issue_key = mentioned.id
project = self.project
noteable_name = noteable.class.name.underscore.downcase
noteable_id = if noteable.is_a?(Commit)
@@ -99,58 +160,28 @@ class JiraService < IssueTrackerService
}
}
- add_comment(data, issue_name)
+ add_comment(data, issue_key)
end
def test_settings
- return unless api_url.present?
- result = JiraService.get(
- jira_api_test_url,
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Basic #{auth}"
- }
- )
+ return unless url.present?
+ # Test settings by getting the project
+ jira_project
- case result.code
- when 201, 200
- Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.")
- true
- else
- Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}")
- false
- end
- rescue Errno::ECONNREFUSED => e
- Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}."
+ rescue Errno::ECONNREFUSED, JIRA::HTTPError => e
+ Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{url}."
false
end
private
- def build_api_url_from_project_url
- server = URI(project_url)
- default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port])
- server_url = "#{server.scheme}://#{server.host}"
- server_url.concat(":#{server.port}") unless default_ports
- "#{server_url}/rest/api/#{DEFAULT_API_VERSION}"
- rescue
- "" # looks like project URL was not valid
- end
-
- def set_api_url
- self.api_url = build_api_url_from_project_url if self.api_url.blank?
- end
-
- def set_jira_issue_transition_id
- self.jira_issue_transition_id ||= "2"
- end
-
def close_issue(entity, issue)
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.diff_head_sha
end
+
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
@@ -161,24 +192,16 @@ class JiraService < IssueTrackerService
end
def transition_issue(issue)
- message = {
- transition: {
- id: jira_issue_transition_id
- }
- }
- send_message(close_issue_url(issue.iid), message.to_json)
+ issue = client.Issue.find(issue.iid)
+ issue.transitions.build.save(transition: { id: jira_issue_transition_id })
end
def add_issue_solved_comment(issue, commit_id, commit_url)
- comment = {
- body: "Issue solved with [#{commit_id}|#{commit_url}]."
- }
-
- send_message(comment_url(issue.iid), comment.to_json)
+ comment = "Issue solved with [#{commit_id}|#{commit_url}]."
+ send_message(issue.iid, comment)
end
- def add_comment(data, issue_name)
- url = comment_url(issue_name)
+ def add_comment(data, issue_key)
user_name = data[:user][:name]
user_url = data[:user][:url]
entity_name = data[:entity][:name]
@@ -186,68 +209,31 @@ class JiraService < IssueTrackerService
entity_title = data[:entity][:title]
project_name = data[:project][:name]
- message = {
- body: %Q{[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'}
- }
+ message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'"
- unless existing_comment?(issue_name, message[:body])
- send_message(url, message.to_json)
+ unless comment_exists?(issue_key, message)
+ send_message(issue_key, message)
end
end
- def auth
- require 'base64'
- Base64.urlsafe_encode64("#{self.username}:#{self.password}")
- end
-
- def send_message(url, message)
- return unless api_url.present?
- result = JiraService.post(
- url,
- body: message,
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Basic #{auth}"
- }
- )
-
- message = case result.code
- when 201, 200, 204
- "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}."
- when 401
- "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again."
- else
- "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}"
- end
-
- Rails.logger.info(message)
- message
- rescue URI::InvalidURIError, Errno::ECONNREFUSED => e
- Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}."
+ def comment_exists?(issue_key, message)
+ comments = client.Issue.find(issue_key).comments
+ comments.map { |comment| comment.body.include?(message) }.any?
end
- def existing_comment?(issue_name, new_comment)
- return unless api_url.present?
- result = JiraService.get(
- comment_url(issue_name),
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Basic #{auth}"
- }
- )
+ def send_message(issue_key, message)
+ return unless url.present?
- case result.code
- when 201, 200
- existing_comments = JSON.parse(result.body)['comments']
+ issue = client.Issue.find(issue_key)
- if existing_comments.present?
- return existing_comments.map { |comment| comment['body'].include?(new_comment) }.any?
- end
+ if issue.comments.build.save!(body: message)
+ result_message = "#{self.class.name} SUCCESS: Successfully posted to #{url}."
end
- false
- rescue JSON::ParserError
- false
+ Rails.logger.info(result_message)
+ result_message
+ rescue URI::InvalidURIError, Errno::ECONNREFUSED, JIRA::HTTPError => e
+ Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}"
end
def resource_url(resource)
@@ -267,16 +253,4 @@ class JiraService < IssueTrackerService
)
)
end
-
- def close_issue_url(issue_name)
- "#{self.api_url}/issue/#{issue_name}/transitions"
- end
-
- def comment_url(issue_name)
- "#{self.api_url}/issue/#{issue_name}/comment"
- end
-
- def jira_api_test_url
- "#{self.api_url}/myself"
- end
end
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index f634e0772c0..f9da273cf08 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -1,4 +1,6 @@
class RedmineService < IssueTrackerService
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 79d041d2775..a6e911df9bd 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -125,14 +125,8 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER
end
- def member?(user, min_member_access = nil)
- member = !!find_member(user.id)
-
- if min_member_access
- member && max_member_access(user.id) >= min_member_access
- else
- member
- end
+ def member?(user, min_member_access = Gitlab::Access::GUEST)
+ max_member_access(user.id) >= min_member_access
end
def human_max_access(user_id)
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 4ae9c20726f..30be7262438 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -11,6 +11,20 @@ class Repository
attr_accessor :path_with_namespace, :project
+ def self.storages
+ Gitlab.config.repositories.storages
+ end
+
+ def self.remove_storage_from_path(repo_path)
+ storages.find do |_, storage_path|
+ if repo_path.start_with?(storage_path)
+ return repo_path.sub(storage_path, '')
+ end
+ end
+
+ repo_path
+ end
+
def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace
@project = project
@@ -181,7 +195,7 @@ class Repository
before_remove_branch
branch = find_branch(branch_name)
- oldrev = branch.try(:target).try(:id)
+ oldrev = branch.try(:dereferenced_target).try(:id)
newrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
@@ -297,10 +311,10 @@ class Repository
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
number_commits_behind = raw_repository.
- count_commits_between(branch.target.sha, root_ref_hash)
+ count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
number_commits_ahead = raw_repository.
- count_commits_between(root_ref_hash, branch.target.sha)
+ count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
{ behind: number_commits_behind, ahead: number_commits_ahead }
end
@@ -682,11 +696,11 @@ class Repository
branches.sort_by(&:name)
when 'updated_desc'
branches.sort do |a, b|
- commit(b.target).committed_date <=> commit(a.target).committed_date
+ commit(b.dereferenced_target).committed_date <=> commit(a.dereferenced_target).committed_date
end
when 'updated_asc'
branches.sort do |a, b|
- commit(a.target).committed_date <=> commit(b.target).committed_date
+ commit(a.dereferenced_target).committed_date <=> commit(b.dereferenced_target).committed_date
end
else
branches
@@ -861,7 +875,7 @@ class Repository
branch = find_branch(ref)
if branch
- last_commit = branch.target
+ last_commit = branch.dereferenced_target
index.read_tree(last_commit.raw_commit.tree)
parents = [last_commit.sha]
end
@@ -948,7 +962,7 @@ class Repository
end
def revert(user, commit, base_branch, revert_tree_id = nil)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
revert_tree_id ||= check_revert_content(commit, base_branch)
return false unless revert_tree_id
@@ -965,7 +979,7 @@ class Repository
end
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
return false unless cherry_pick_tree_id
@@ -994,7 +1008,7 @@ class Repository
end
def check_revert_content(commit, base_branch)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
args = [commit.id, source_sha]
args << { mainline: 1 } if commit.merge_commit?
@@ -1008,7 +1022,7 @@ class Repository
end
def check_cherry_pick_content(commit, base_branch)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
args = [commit.id, source_sha]
args << 1 if commit.merge_commit?
@@ -1081,7 +1095,7 @@ class Repository
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
oldrev = Gitlab::Git::BLANK_SHA
else
- oldrev = rugged.merge_base(newrev, target_branch.target.sha)
+ oldrev = rugged.merge_base(newrev, target_branch.dereferenced_target.sha)
end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
@@ -1141,7 +1155,7 @@ class Repository
end
def tags_sorted_by_committed_date
- tags.sort_by { |tag| tag.target.committed_date }
+ tags.sort_by { |tag| tag.dereferenced_target.committed_date }
end
def keep_around_ref_name(sha)
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 11c072dd000..f5ade1cc293 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -53,7 +53,7 @@ class Todo < ActiveRecord::Base
# Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue"
def order_by_labels_priority
params = {
- target_type: ['Issue', 'MergeRequest'],
+ target_type_column: "todos.target_type",
target_column: "todos.target_id",
project_column: "todos.project_id"
}
diff --git a/app/models/user.rb b/app/models/user.rb
index 9e76df63d31..af3c0b7dc02 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -93,8 +93,10 @@ class User < ActiveRecord::Base
#
# Validations
#
+ # Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true
- validates :notification_email, presence: true, email: true
+ validates :notification_email, presence: true
+ validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
@@ -256,6 +258,24 @@ class User < ActiveRecord::Base
)
end
+ # searches user by given pattern
+ # it compares name, email, username fields and user's secondary emails with given pattern
+ # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
+
+ def search_with_secondary_emails(query)
+ table = arel_table
+ email_table = Email.arel_table
+ pattern = "%#{query}%"
+ matched_by_emails_user_ids = email_table.project(email_table[:user_id]).where(email_table[:email].matches(pattern))
+
+ where(
+ table[:name].matches(pattern).
+ or(table[:email].matches(pattern)).
+ or(table[:username].matches(pattern)).
+ or(table[:id].in(matched_by_emails_user_ids))
+ )
+ end
+
def by_login(login)
return nil unless login
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index d3dd30b2588..8face432d97 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -10,17 +10,14 @@ module Ci
create_builds!
end
- @pipeline.with_lock do
- new_builds =
- stage_indexes_of_created_builds.map do |index|
- process_stage(index)
- end
+ new_builds =
+ stage_indexes_of_created_builds.map do |index|
+ process_stage(index)
+ end
- @pipeline.update_status
+ @pipeline.update_status
- # Return a flag if a when builds got enqueued
- new_builds.flatten.any?
- end
+ new_builds.flatten.any?
end
private
@@ -32,9 +29,11 @@ module Ci
def process_stage(index)
current_status = status_for_prior_stages(index)
- created_builds_in_stage(index).select do |build|
- if HasStatus::COMPLETED_STATUSES.include?(current_status)
- process_build(build, current_status)
+ if HasStatus::COMPLETED_STATUSES.include?(current_status)
+ created_builds_in_stage(index).select do |build|
+ Gitlab::OptimisticLocking.retry_lock(build) do |subject|
+ process_build(subject, current_status)
+ end
end
end
end
diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb
index 6973191b203..74b5ebf372b 100644
--- a/app/services/ci/register_build_service.rb
+++ b/app/services/ci/register_build_service.rb
@@ -28,17 +28,14 @@ module Ci
if build
# In case when 2 runners try to assign the same build, second runner will be declined
- # with StateMachines::InvalidTransition in run! method.
- build.with_lock do
- build.runner_id = current_runner.id
- build.save!
- build.run!
- end
+ # with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
+ build.runner_id = current_runner.id
+ build.run!
end
build
- rescue StateMachines::InvalidTransition
+ rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
nil
end
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 918eddaa53a..3e5dd4ebb86 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -42,7 +42,7 @@ class DeleteBranchService < BaseService
Gitlab::DataBuilder::Push.build(
project,
current_user,
- branch.target.sha,
+ branch.dereferenced_target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}",
[])
diff --git a/app/services/delete_tag_service.rb b/app/services/delete_tag_service.rb
index d0cb151a010..d824406cb49 100644
--- a/app/services/delete_tag_service.rb
+++ b/app/services/delete_tag_service.rb
@@ -36,7 +36,7 @@ class DeleteTagService < BaseService
Gitlab::DataBuilder::Push.build(
project,
current_user,
- tag.target.sha,
+ tag.dereferenced_target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
[])
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index e6002b03b93..20a4445bddf 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -27,8 +27,8 @@ class GitTagPushService < BaseService
tag_name = Gitlab::Git.ref_name(params[:ref])
tag = project.repository.find_tag(tag_name)
- if tag && tag.object_sha == params[:newrev]
- commit = project.commit(tag.target)
+ if tag && tag.target == params[:newrev]
+ commit = project.commit(tag.dereferenced_target)
commits = [commit].compact
message = tag.message
end
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index 74291312c4e..d622f9edd33 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -2,21 +2,24 @@ module Labels
class FindOrCreateService
def initialize(current_user, project, params = {})
@current_user = current_user
- @group = project.group
@project = project
@params = params.dup
end
- def execute
+ def execute(skip_authorization: false)
+ @skip_authorization = skip_authorization
find_or_create_label
end
private
- attr_reader :current_user, :group, :project, :params
+ attr_reader :current_user, :project, :params, :skip_authorization
def available_labels
- @available_labels ||= LabelsFinder.new(current_user, project_id: project.id).execute
+ @available_labels ||= LabelsFinder.new(
+ current_user,
+ project_id: project.id
+ ).execute(skip_authorization: skip_authorization)
end
def find_or_create_label
diff --git a/app/services/members/approve_access_request_service.rb b/app/services/members/approve_access_request_service.rb
index 416aee2ab51..c13f289f61e 100644
--- a/app/services/members/approve_access_request_service.rb
+++ b/app/services/members/approve_access_request_service.rb
@@ -4,17 +4,25 @@ module Members
attr_accessor :source
+ # source - The source object that respond to `#requesters` (i.g. project or group)
+ # current_user - The user that performs the access request approval
+ # params - A hash of parameters
+ # :user_id - User ID used to retrieve the access requester
+ # :id - Member ID used to retrieve the access requester
+ # :access_level - Optional access level set when the request is accepted
def initialize(source, current_user, params = {})
@source = source
@current_user = current_user
- @params = params
+ @params = params.slice(:user_id, :id, :access_level)
end
- def execute
+ # opts - A hash of options
+ # :force - Bypass permission check: current_user can be nil in that case
+ def execute(opts = {})
condition = params[:user_id] ? { user_id: params[:user_id] } : { id: params[:id] }
access_requester = source.requesters.find_by!(condition)
- raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester)
+ raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester, opts)
access_requester.access_level = params[:access_level] if params[:access_level]
access_requester.accept_request
@@ -24,8 +32,11 @@ module Members
private
- def can_update_access_requester?(access_requester)
- access_requester && can?(current_user, action_member_permission(:update, access_requester), access_requester)
+ def can_update_access_requester?(access_requester, opts = {})
+ access_requester && (
+ opts[:force] ||
+ can?(current_user, action_member_permission(:update, access_requester), access_requester)
+ )
end
end
end
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
new file mode 100644
index 00000000000..e4b24ccef92
--- /dev/null
+++ b/app/services/members/create_service.rb
@@ -0,0 +1,16 @@
+module Members
+ class CreateService < BaseService
+ def execute
+ return false if params[:user_ids].blank?
+
+ project.team.add_users(
+ params[:user_ids].split(','),
+ params[:access_level],
+ expires_at: params[:expires_at],
+ current_user: current_user
+ )
+
+ true
+ end
+ end
+end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 404f75616b5..f415244068b 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -13,20 +13,8 @@ module MergeRequests
merge_request.target_project ||= (project.forked_from_project || project)
merge_request.target_branch ||= merge_request.target_project.default_branch
- if merge_request.target_branch.blank? || merge_request.source_branch.blank?
- message =
- if params[:source_branch] || params[:target_branch]
- "You must select source and target branch"
- end
-
- return build_failed(merge_request, message)
- end
-
- if merge_request.source_project == merge_request.target_project &&
- merge_request.target_branch == merge_request.source_branch
-
- return build_failed(merge_request, 'You must select different branches')
- end
+ messages = validate_branches(merge_request)
+ return build_failed(merge_request, messages) unless messages.empty?
compare = CompareService.new.execute(
merge_request.source_project,
@@ -43,6 +31,34 @@ module MergeRequests
private
+ def validate_branches(merge_request)
+ messages = []
+
+ if merge_request.target_branch.blank? || merge_request.source_branch.blank?
+ messages <<
+ if params[:source_branch] || params[:target_branch]
+ "You must select source and target branch"
+ end
+ end
+
+ if merge_request.source_project == merge_request.target_project &&
+ merge_request.target_branch == merge_request.source_branch
+
+ messages << 'You must select different branches'
+ end
+
+ # See if source and target branches exist
+ unless merge_request.source_project.commit(merge_request.source_branch)
+ messages << "Source branch \"#{merge_request.source_branch}\" does not exist"
+ end
+
+ unless merge_request.target_project.commit(merge_request.target_branch)
+ messages << "Target branch \"#{merge_request.target_branch}\" does not exist"
+ end
+
+ messages
+ end
+
# When your branch name starts with an iid followed by a dash this pattern will be
# interpreted as the user wants to close that issue on this project.
#
@@ -91,8 +107,10 @@ module MergeRequests
merge_request
end
- def build_failed(merge_request, message)
- merge_request.errors.add(:base, message) unless message.nil?
+ def build_failed(merge_request, messages)
+ messages.compact.each do |message|
+ merge_request.errors.add(:base, message)
+ end
merge_request.compare_commits = []
merge_request.can_be_created = false
merge_request
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index e466ffa60eb..d7221fe993c 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -29,7 +29,7 @@ module Projects
if unknown_url?
# In this case, we only want to import issues, not a repository.
create_repository
- else
+ elsif !project.repository_exists?
import_repository
end
end
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index adfa1eaafc9..05c88ca1cc8 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -16,7 +16,8 @@
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
= visibility_level_icon(group.visibility_level, fw: false)
- = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+ .image-container.s40
+ = image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title
= link_to [:admin, group], class: 'group-name' do
= group.name
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 0188ed448ce..a7c1a4f5038 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -13,7 +13,8 @@
Group info:
%ul.well-list
%li
- = image_tag group_icon(@group), class: "avatar s60"
+ .image-container.s60
+ = image_tag group_icon(@group), class: "avatar s60"
%li
%span.light Name:
%strong= @group.name
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 339cfc613fe..10dce6f3d8f 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -76,7 +76,8 @@
.title
= link_to [:admin, project.namespace.becomes(Namespace), project] do
.dash-project-avatar
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+ .image-container.s40
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40')
%span.project-full-name
%span.namespace-name
- if project.namespace
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 357123c2c13..d3038ae644f 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -10,7 +10,7 @@
= hidden_field_tag "filter", h(params[:filter])
.search-holder
.search-field-holder
- = search_field_tag :name, params[:name], placeholder: 'Search by name, email or username', class: 'form-control search-text-input js-search-input', spellcheck: false
+ = search_field_tag :search_query, params[:search_query], placeholder: 'Search by name, email or username', class: 'form-control search-text-input js-search-input', spellcheck: false
= icon("search", class: "search-icon")
.dropdown
- toggle_text = if @sort.present? then sort_options_hash[@sort] else sort_title_name end
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 2a0302638ba..2411cc45724 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -1,69 +1,70 @@
- page_title "Todos"
- header_title "Todos", dashboard_todos_path
-.top-area
- %ul.nav-links
- - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
- %li{class: "todos-pending #{todo_pending_active}"}
- = link_to todos_filter_path(state: 'pending') do
- %span
- To do
- %span.badge
- = number_with_delimiter(todos_pending_count)
- - todo_done_active = ('active' if params[:state] == 'done')
- %li{class: "todos-done #{todo_done_active}"}
- = link_to todos_filter_path(state: 'done') do
- %span
- Done
- %span.badge
- = number_with_delimiter(todos_done_count)
+- if current_user.todos.any?
+ .top-area
+ %ul.nav-links
+ - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
+ %li{class: "todos-pending #{todo_pending_active}"}
+ = link_to todos_filter_path(state: 'pending') do
+ %span
+ To do
+ %span.badge
+ = number_with_delimiter(todos_pending_count)
+ - todo_done_active = ('active' if params[:state] == 'done')
+ %li{class: "todos-done #{todo_done_active}"}
+ = link_to todos_filter_path(state: 'done') do
+ %span
+ Done
+ %span.badge
+ = number_with_delimiter(todos_done_count)
- .nav-controls
- - if @todos.any?(&:pending?)
- = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do
- Mark all as done
- = icon('spinner spin')
+ .nav-controls
+ - if @todos.any?(&:pending?)
+ = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do
+ Mark all as done
+ = icon('spinner spin')
-.todos-filters
- .row-content-block.second-block
- = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do
- .filter-item.inline
- - if params[:project_id].present?
- = hidden_field_tag(:project_id, params[:project_id])
- = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
- placeholder: 'Search projects', data: { data: todo_projects_options } })
- .filter-item.inline
- - if params[:author_id].present?
- = hidden_field_tag(:author_id, params[:author_id])
- = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
- placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } })
- .filter-item.inline
- - if params[:type].present?
- = hidden_field_tag(:type, params[:type])
- = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
- data: { data: todo_types_options } })
- .filter-item.inline.actions-filter
- - if params[:action_id].present?
- = hidden_field_tag(:action_id, params[:action_id])
- = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
- data: { data: todo_actions_options }})
- .pull-right
- .dropdown.inline.prepend-left-10
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light
- - if @sort.present?
- = sort_options_hash[@sort]
- - else
- = sort_title_recently_created
- = icon('caret-down')
- %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
- %li
- = link_to todos_filter_path(sort: sort_value_priority) do
- = sort_title_priority
- = link_to todos_filter_path(sort: sort_value_recently_created) do
+ .todos-filters
+ .row-content-block.second-block
+ = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do
+ .filter-item.inline
+ - if params[:project_id].present?
+ = hidden_field_tag(:project_id, params[:project_id])
+ = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
+ placeholder: 'Search projects', data: { data: todo_projects_options } })
+ .filter-item.inline
+ - if params[:author_id].present?
+ = hidden_field_tag(:author_id, params[:author_id])
+ = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
+ placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } })
+ .filter-item.inline
+ - if params[:type].present?
+ = hidden_field_tag(:type, params[:type])
+ = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
+ data: { data: todo_types_options } })
+ .filter-item.inline.actions-filter
+ - if params[:action_id].present?
+ = hidden_field_tag(:action_id, params[:action_id])
+ = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
+ data: { data: todo_actions_options }})
+ .pull-right
+ .dropdown.inline.prepend-left-10
+ %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %span.light
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
= sort_title_recently_created
- = link_to todos_filter_path(sort: sort_value_oldest_created) do
- = sort_title_oldest_created
+ = icon('caret-down')
+ %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
+ %li
+ = link_to todos_filter_path(sort: sort_value_priority) do
+ = sort_title_priority
+ = link_to todos_filter_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to todos_filter_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
.prepend-top-default
@@ -78,5 +79,29 @@
%ul.content-list.todos-list
= render group[1]
= paginate @todos, theme: "gitlab"
+ - elsif current_user.todos.any?
+ .todos-all-done
+ = render "shared/empty_states/todos_all_done.svg"
+ %h4.text-center
+ Good job! Looks like you don't have any todos left.
+ %p.text-center
+ Are you looking for things to do? Take a look at
+ = succeed "," do
+ = link_to "the opened issues", issues_dashboard_path
+ contribute to
+ = link_to "merge requests", merge_requests_dashboard_path
+ or mention someone in a comment to assign a new todo automatically.
- else
- .nothing-here-block You're all done!
+ .todos-empty
+ .todos-empty-hero
+ = render "shared/empty_states/todos_empty.svg"
+ .todos-empty-content
+ %h4
+ Todos let you see what you should do next.
+ %p
+ When an issue or merge request is assigned to you, or when you
+ %strong
+ @mention
+ in a comment, this will trigger a new item in your todo list, automatically.
+ %p
+ You will always know what to work on next.
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index c766370d5a0..f84ac37fa8f 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -8,7 +8,8 @@
.form-group
.col-sm-offset-2.col-sm-10
- = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
+ .image-container.s160
+ = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
%p.light
- if @group.avatar?
You can change your group avatar here
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 70783a63409..45325d6bc4b 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -13,7 +13,7 @@
.other-labels
- if @labels.present?
%ul.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', collection: @labels, as: :label
+ = render partial: 'shared/label', subject: @group, collection: @labels, as: :label
= paginate @labels, theme: 'gitlab'
- else
.nothing-here-block
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index fab61f447c2..275581b3af8 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -6,7 +6,8 @@
.cover-block.groups-cover-block
%div{ class: container_class }
- = image_tag group_icon(@group), class: "avatar group-avatar s70 avatar-tile"
+ .image-container.s70.group-avatar
+ = image_tag group_icon(@group), class: "avatar s70 avatar-tile"
.group-info
.cover-title
%h1
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index d16bd61b779..070ed90da6d 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -461,7 +461,7 @@
.panel-body
= lorem
- %h2#alert Alerts
+ %h2#alerts Alerts
.row
.col-md-6
diff --git a/app/views/kaminari/gitlab/_gap.html.haml b/app/views/kaminari/gitlab/_gap.html.haml
index 80ca30f36e6..889514c4755 100644
--- a/app/views/kaminari/gitlab/_gap.html.haml
+++ b/app/views/kaminari/gitlab/_gap.html.haml
@@ -4,6 +4,6 @@
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-%li{class: "page"}
- %span.page.gap
+%li
+ %span.gap
= raw(t 'views.pagination.truncate')
diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml
index 522e4d1d05f..750aed8f329 100644
--- a/app/views/kaminari/gitlab/_page.html.haml
+++ b/app/views/kaminari/gitlab/_page.html.haml
@@ -6,5 +6,5 @@
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-%li{class: "page#{' active' if page.current?}"}
+%li{class: "page#{' active' if page.current?}#{' sibling' if page.next? || page.prev?}"}
= link_to page, url, {remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil}
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 7faa8bded86..7a9859262f7 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -29,10 +29,6 @@
= icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_pending_count
- - if current_user.can_create_project?
- %li
- = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
- = icon('plus fw')
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, title: 'Sherlock Transactions',
@@ -48,6 +44,8 @@
= link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
%li
= link_to "Profile Settings", profile_path, aria: { label: "Profile Settings" }
+ %li
+ = link_to "Help", help_path, aria: { label: "Help" }
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 67f558c854b..a0356feef95 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,49 +1,38 @@
-%ul.nav.nav-sidebar
- = 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(controller: :todos) do
- = link_to dashboard_todos_path, title: 'Todos' do
- %span
- Todos
- %span.count= number_with_delimiter(todos_pending_count)
- = 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
+.nav-sidebar
+ .sidebar-header Across GitLab
+ %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
- 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(current_user.assigned_issues.opened.count)
- = nav_link(path: 'dashboard#merge_requests') do
- = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
- %span
- Merge Requests
- %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
- = nav_link(controller: 'dashboard/snippets') do
- = link_to dashboard_snippets_path, title: 'Snippets' do
- %span
- Snippets
- = nav_link(controller: :help) do
- = link_to help_path, title: 'Help' do
- %span
- Help
- = nav_link(html_options: {class: profile_tab_class}) do
- = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
- %span
- Profile Settings
+ 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(current_user.assigned_issues.opened.count)
+ = nav_link(path: 'dashboard#merge_requests') do
+ = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
+ %span
+ Merge Requests
+ %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
+ = nav_link(controller: 'dashboard/snippets') do
+ = link_to dashboard_snippets_path, title: 'Snippets' do
+ %span
+ Snippets
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index d3987fc9c4f..e67b66d1fff 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,7 +1,8 @@
- empty_repo = @project.empty_repo?
.project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
%div{ class: container_class }
- = project_icon(@project, alt: @project.name, class: 'project-avatar avatar s70 avatar-tile')
+ .image-container.s70.project-avatar
+ = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile')
%h1.project-title
= @project.name
%span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml
index ba1502c97b6..f7071051efc 100644
--- a/app/views/projects/boards/components/_board.html.haml
+++ b/app/views/projects/boards/components/_board.html.haml
@@ -11,7 +11,9 @@
.board-inner
%header.board-header{ ":class" => "{ 'has-border': list.label }", ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" }
%h3.board-title.js-board-handle{ ":class" => "{ 'user-can-drag': (!disabled && !list.preset) }" }
- {{ list.title }}
+ %span.has-tooltip{ ":title" => "(list.label ? list.label.description : '')",
+ data: { container: "body", placement: "bottom" } }
+ {{ list.title }}
.board-issue-count-holder.pull-right.clearfix{ "v-if" => "list.type !== 'blank'" }
%span.board-issue-count.pull-left{ ":class" => "{ 'has-btn': list.type !== 'done' }" }
{{ list.issuesSize }}
diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml
index c6d718a1cd1..8fce702314c 100644
--- a/app/views/projects/boards/components/_card.html.haml
+++ b/app/views/projects/boards/components/_card.html.haml
@@ -7,8 +7,11 @@
":issue-link-base" => "issueLinkBase",
":disabled" => "disabled",
"track-by" => "id" }
- %li.card{ ":class" => "{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id }",
- ":index" => "index" }
+ %li.card{ ":class" => "{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }",
+ ":index" => "index",
+ "@mousedown" => "mouseDown",
+ "@mouseMove" => "mouseMove",
+ "@mouseup" => "showIssue($event)" }
%h4.card-title
= icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential")
%a{ ":href" => "issueLinkBase + '/' + issue.id",
@@ -18,6 +21,11 @@
%span.card-number{ "v-if" => "issue.id" }
= precede '#' do
{{ issue.id }}
+ %a.has-tooltip{ ":href" => "'#{root_path}' + issue.assignee.username",
+ ":title" => "'Assigned to ' + issue.assignee.name",
+ "v-if" => "issue.assignee",
+ data: { container: 'body' } }
+ %img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20 }
%button.label.color-label.has-tooltip{ "v-for" => "label in issue.labels",
type: "button",
"v-if" => "(!list.label || label.id !== list.label.id)",
@@ -26,8 +34,3 @@
":title" => "label.description",
data: { container: 'body' } }
{{ label.title }}
- %a.has-tooltip{ ":href" => "'#{root_path}' + issue.assignee.username",
- ":title" => "'Assigned to ' + issue.assignee.name",
- "v-if" => "issue.assignee",
- data: { container: 'body' } }
- %img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20 }
diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml
new file mode 100644
index 00000000000..f0c0c6953e0
--- /dev/null
+++ b/app/views/projects/boards/components/_sidebar.html.haml
@@ -0,0 +1,23 @@
+%board-sidebar{ "inline-template" => true,
+ ":current-user" => "#{current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) if current_user}" }
+ %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
+ .issuable-sidebar
+ .block.issuable-sidebar-header
+ %span.issuable-header-text.hide-collapsed.pull-left
+ %strong
+ {{ issue.title }}
+ %br/
+ %span
+ = precede "#" do
+ {{ issue.id }}
+ %a.gutter-toggle.pull-right{ role: "button",
+ href: "#",
+ "@click.prevent" => "closeSidebar",
+ "aria-label" => "Toggle sidebar" }
+ = custom_icon("icon_close", size: 15)
+ .js-issuable-update
+ = render "projects/boards/components/sidebar/assignee"
+ = render "projects/boards/components/sidebar/milestone"
+ = render "projects/boards/components/sidebar/due_date"
+ = render "projects/boards/components/sidebar/labels"
+ = render "projects/boards/components/sidebar/notifications"
diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml
new file mode 100644
index 00000000000..604e13858d1
--- /dev/null
+++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml
@@ -0,0 +1,40 @@
+.block.assignee
+ .title.hide-collapsed
+ Assignee
+ = icon("spinner spin", class: "block-loading")
+ - if can?(current_user, :admin_issue, @project)
+ = link_to "Edit", "#", class: "edit-link pull-right"
+ .value.hide-collapsed
+ %span.assign-yourself.no-value{ "v-if" => "!issue.assignee" }
+ No assignee
+ - if can?(current_user, :admin_issue, @project)
+ \-
+ %a.js-assign-yourself{ href: "#" }
+ assign yourself
+ %a.author_link.bold{ ":href" => "'#{root_url}' + issue.assignee.username",
+ "v-if" => "issue.assignee" }
+ %img.avatar.avatar-inline.s32{ ":src" => "issue.assignee.avatar",
+ width: "32" }
+ %span.author
+ {{ issue.assignee.name }}
+ %span.username
+ = precede "@" do
+ {{ issue.assignee.username }}
+ - if can?(current_user, :admin_issue, @project)
+ .selectbox.hide-collapsed
+ %input{ type: "hidden",
+ name: "issue[assignee_id]",
+ id: "issue_assignee_id",
+ ":value" => "issue.assignee.id",
+ "v-if" => "issue.assignee" }
+ .dropdown
+ %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true" },
+ ":data-issuable-id" => "issue.id",
+ ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
+ Select assignee
+ = icon("chevron-down")
+ .dropdown-menu.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author
+ = dropdown_title("Assign to")
+ = dropdown_filter("Search users")
+ = dropdown_content
+ = dropdown_loading
diff --git a/app/views/projects/boards/components/sidebar/_due_date.html.haml b/app/views/projects/boards/components/sidebar/_due_date.html.haml
new file mode 100644
index 00000000000..c7da1d0d4ac
--- /dev/null
+++ b/app/views/projects/boards/components/sidebar/_due_date.html.haml
@@ -0,0 +1,32 @@
+.block.due_date
+ .title
+ Due date
+ = icon("spinner spin", class: "block-loading")
+ - if can?(current_user, :admin_issue, @project)
+ = link_to "Edit", "#", class: "edit-link pull-right"
+ .value
+ .value-content
+ %span.no-value{ "v-if" => "!issue.dueDate" }
+ No due date
+ %span.bold{ "v-if" => "issue.dueDate" }
+ {{ issue.dueDate | due-date }}
+ - if can?(current_user, :admin_issue, @project)
+ %span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" }
+ \-
+ %a.js-remove-due-date{ href: "#", role: "button" }
+ remove due date
+ - if can?(current_user, :admin_issue, @project)
+ .selectbox
+ %input{ type: "hidden",
+ name: "issue[due_date]",
+ ":value" => "issue.dueDate" }
+ .dropdown
+ %button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button',
+ data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" },
+ ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
+ %span.dropdown-toggle-text Due date
+ = icon('chevron-down')
+ .dropdown-menu.dropdown-menu-due-date
+ = dropdown_title('Due date')
+ = dropdown_content do
+ .js-due-date-calendar
diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml
new file mode 100644
index 00000000000..ce68e5e1998
--- /dev/null
+++ b/app/views/projects/boards/components/sidebar/_labels.html.haml
@@ -0,0 +1,30 @@
+.block.labels
+ .title
+ Labels
+ = icon("spinner spin", class: "block-loading")
+ - if can?(current_user, :admin_issue, @project)
+ = link_to "Edit", "#", class: "edit-link pull-right"
+ .value.issuable-show-labels
+ %span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" }
+ None
+ %a{ href: "#",
+ "v-for" => "label in issue.labels" }
+ %span.label.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
+ {{ label.title }}
+ - if can?(current_user, :admin_issue, @project)
+ .selectbox
+ %input{ type: "hidden",
+ name: "issue[label_names][]",
+ "v-for" => "label in issue.labels",
+ ":value" => "label.id" }
+ .dropdown
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button",
+ data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: namespace_project_labels_path(@project.namespace, @project, :json), namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path) },
+ ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
+ %span.dropdown-toggle-text
+ Label
+ = icon('chevron-down')
+ .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
+ = render partial: "shared/issuable/label_page_default"
+ - if can? current_user, :admin_label, @project and @project
+ = render partial: "shared/issuable/label_page_create"
diff --git a/app/views/projects/boards/components/sidebar/_milestone.html.haml b/app/views/projects/boards/components/sidebar/_milestone.html.haml
new file mode 100644
index 00000000000..3cd20d1c0f7
--- /dev/null
+++ b/app/views/projects/boards/components/sidebar/_milestone.html.haml
@@ -0,0 +1,28 @@
+.block.milestone
+ .title
+ Milestone
+ = icon("spinner spin", class: "block-loading")
+ - if can?(current_user, :admin_issue, @project)
+ = link_to "Edit", "#", class: "edit-link pull-right"
+ .value
+ %span.no-value{ "v-if" => "!issue.milestone" }
+ None
+ %span.bold.has-tooltip{ "v-if" => "issue.milestone" }
+ {{ issue.milestone.title }}
+ - if can?(current_user, :admin_issue, @project)
+ .selectbox
+ %input{ type: "hidden",
+ ":value" => "issue.milestone.id",
+ name: "issue[milestone_id]",
+ "v-if" => "issue.milestone" }
+ .dropdown
+ %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" },
+ ":data-issuable-id" => "issue.id",
+ ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
+ Milestone
+ = icon("chevron-down")
+ .dropdown-menu.dropdown-select.dropdown-menu-selectable
+ = dropdown_title("Assignee milestone")
+ = dropdown_filter("Search milestones")
+ = dropdown_content
+ = dropdown_loading
diff --git a/app/views/projects/boards/components/sidebar/_notifications.html.haml b/app/views/projects/boards/components/sidebar/_notifications.html.haml
new file mode 100644
index 00000000000..21c9563e9db
--- /dev/null
+++ b/app/views/projects/boards/components/sidebar/_notifications.html.haml
@@ -0,0 +1,11 @@
+- if current_user
+ .block.light.subscription{ ":data-url" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '/toggle_subscription'" }
+ .title
+ Notifications
+ %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" }
+ {{ issue.subscribed ? 'Unsubscribe' : 'Subscribe' }}
+ .subscription-status{ ":data-status" => "issue.subscribed ? 'subscribed' : 'unsubscribed'" }
+ .unsubscribed{ "v-show" => "!issue.subscribed" }
+ You're not receiving notifications from this thread.
+ .subscribed{ "v-show" => "issue.subscribed" }
+ You're receiving notifications because you're subscribed to this thread.
diff --git a/app/views/projects/boards/index.html.haml b/app/views/projects/boards/index.html.haml
index 885f8e34b55..29c9a43a0c1 100644
--- a/app/views/projects/boards/index.html.haml
+++ b/app/views/projects/boards/index.html.haml
@@ -10,7 +10,9 @@
= render 'shared/issuable/filter', type: :boards
-.boards-list#board-app{ "v-cloak" => true, data: board_data }
- .boards-app-loading.text-center{ "v-if" => "loading" }
- = icon("spinner spin")
- = render "projects/boards/components/board"
+#board-app.boards-app{ "v-cloak" => true, data: board_data }
+ .boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" }
+ .boards-app-loading.text-center{ "v-if" => "loading" }
+ = icon("spinner spin")
+ = render "projects/boards/components/board"
+ = render "projects/boards/components/sidebar"
diff --git a/app/views/projects/boards/show.html.haml b/app/views/projects/boards/show.html.haml
index 885f8e34b55..29c9a43a0c1 100644
--- a/app/views/projects/boards/show.html.haml
+++ b/app/views/projects/boards/show.html.haml
@@ -10,7 +10,9 @@
= render 'shared/issuable/filter', type: :boards
-.boards-list#board-app{ "v-cloak" => true, data: board_data }
- .boards-app-loading.text-center{ "v-if" => "loading" }
- = icon("spinner spin")
- = render "projects/boards/components/board"
+#board-app.boards-app{ "v-cloak" => true, data: board_data }
+ .boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" }
+ .boards-app-loading.text-center{ "v-if" => "loading" }
+ = icon("spinner spin")
+ = render "projects/boards/components/board"
+ = render "projects/boards/components/sidebar"
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 4480b2f22c3..9135cee8364 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -1,4 +1,4 @@
-- commit = @repository.commit(branch.target)
+- commit = @repository.commit(branch.dereferenced_target)
- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0
- diverging_commit_counts = @repository.diverging_commit_counts(branch)
- number_commits_behind = diverging_commit_counts[:behind]
@@ -31,7 +31,12 @@
= render 'projects/buttons/download', project: @project, ref: branch.name
- if can?(current_user, :push_code, @project)
- = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: "btn btn-remove remove-row has-tooltip #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}", title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
+ = link_to namespace_project_branch_path(@project.namespace, @project, branch.name),
+ class: "btn btn-remove remove-row #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}",
+ method: :delete,
+ data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" },
+ remote: true,
+ "aria-label" => "Delete branch" do
= icon("trash-o")
- if branch.name != @repository.root_ref
diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml
index 51b5bd9db42..3f2ce7377fd 100644
--- a/app/views/projects/builds/_header.html.haml
+++ b/app/views/projects/builds/_header.html.haml
@@ -1,16 +1,19 @@
.content-block.build-header
- = ci_status_with_icon(@build.status)
- Build
- %strong ##{@build.id}
- for commit
- = link_to ci_status_path(@build.pipeline) do
- %strong= @build.pipeline.short_sha
- from
- = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
- %code
- = @build.ref
- - if @build.user
- = render "user"
- = time_ago_with_tooltip(@build.created_at)
+ .header-content
+ = ci_status_with_icon(@build.status)
+ Build
+ %strong ##{@build.id}
+ for commit
+ = link_to ci_status_path(@build.pipeline) do
+ %strong= @build.pipeline.short_sha
+ from
+ = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
+ %code
+ = @build.ref
+ - if @build.user
+ = render "user"
+ = time_ago_with_tooltip(@build.created_at)
+ - if can?(current_user, :update_build, @build) && @build.retryable?
+ = link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted pull-right', method: :post
%button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index b1053028279..28f519f11b2 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -44,7 +44,7 @@
.title
Build details
- if can?(current_user, :update_build, @build) && @build.retryable?
- = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
+ = link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
- if @build.merge_request
%p.build-detail-row
%span.build-light-text Merge Request:
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 29d549a60f5..27da86b9efe 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -5,10 +5,10 @@
= custom_icon('icon_fork')
%span Fork
- else
- = link_to new_namespace_project_fork_path(@project.namespace, @project), title: 'Fork project', class: 'btn has-tooltip' do
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: 'Fork project', class: 'btn' do
= custom_icon('icon_fork')
%span Fork
%div.count-with-arrow
%span.arrow
- = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks', class: 'count has-tooltip' do
+ = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks', class: 'count' do
= @project.forks_count
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 311583037e5..12d35101770 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
- if current_user
- = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: current_user.starred?(@project) ? 'Unstar project' : 'Star project' } do
+ = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do
- if current_user.starred?(@project)
= icon('star')
%span.starred Unstar
diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml
index 55965172d3f..93dca81e6f9 100644
--- a/app/views/projects/ci/builds/_build_pipeline.html.haml
+++ b/app/views/projects/ci/builds/_build_pipeline.html.haml
@@ -1,14 +1,13 @@
- is_playable = subject.playable? && can?(current_user, :update_build, @project)
- if is_playable
= link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do
- = render_status_with_link('build', 'play')
+ = ci_icon_for_status('play')
.ci-status-text= subject.name
- elsif can?(current_user, :read_build, @project)
= link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do
%span.ci-status-icon
- = render_status_with_link('build', subject.status)
+ = ci_icon_for_status(subject.status)
.ci-status-text= subject.name
- else
%span.ci-status-icon
- = render_status_with_link('build', subject.status)
- = ci_icon_for_status(subject.status)
+ = ci_icon_for_status(subject.status)
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index c6f359f5679..840f468dc05 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -5,10 +5,7 @@
%tr.commit
%td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
- - if defined?(status_icon_only) && status_icon_only
- = ci_icon_for_status(status)
- - else
- = ci_status_with_icon(status)
+ = ci_status_with_icon(status)
%td
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
@@ -40,7 +37,7 @@
%p.commit-title
- if commit = pipeline.commit
= author_avatar(commit, size: 20)
- = link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message"
+ = link_to_gfm truncate(commit.title, length: 60, escape: false), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message"
- else
Cant find HEAD commit for this branch
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 6c82a4e5600..d8c95376b94 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,8 +1,23 @@
.commit-info-row.commit-info-row-header
- %span.hidden-xs Authored by
+ %span.hidden-xs.hidden-sm Commit
+ = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace js-details-short"
+ = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do
+ %span.text-expander
+ \...
+ %span.js-details-content.hide
+ = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace hidden-xs hidden-sm"
+ = clipboard_button(clipboard_text: @commit.id)
+ %span.hidden-xs authored
+ #{time_ago_with_tooltip(@commit.authored_date)}
+ %span by
+ = author_avatar(@commit, size: 24)
%strong
= commit_author_link(@commit, avatar: true, size: 24)
- #{time_ago_with_tooltip(@commit.authored_date)}
+ - if @commit.different_committer?
+ %span.light Committed by
+ %strong
+ = commit_committer_link(@commit, avatar: true, size: 24)
+ #{time_ago_with_tooltip(@commit.committed_date)}
.pull-right.commit-action-buttons
- if defined?(@notes_count) && @notes_count > 0
@@ -33,42 +48,35 @@
%li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
%li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
-- if @commit.different_committer?
- .commit-info-row
- %span.light Committed by
- %strong
- = commit_committer_link(@commit, avatar: true, size: 24)
- #{time_ago_with_tooltip(@commit.committed_date)}
-
-.commit-info-row
- %span.hidden-xs.hidden-sm Commit
- = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace hidden-xs hidden-sm"
- = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace visible-xs-inline visible-sm-inline"
- = clipboard_button(clipboard_text: @commit.id)
- %span.cgray= pluralize(@commit.parents.count, "parent")
- - @commit.parents.each do |parent|
- = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
-
- %span.commit-info.branches
- %i.fa.fa-spinner.fa-spin
-
-- if @commit.status
- .commit-info-row
- Builds for
- = pluralize(@commit.pipelines.count, 'pipeline')
- = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do
- = ci_icon_for_status(@commit.status)
- %span.ci-status-label
- = ci_label_for_status(@commit.status)
- in
- = time_interval_in_words @commit.pipelines.total_duration
-
-.commit-box.content-block
+.commit-box
%h3.commit-title
= markdown(@commit.title, pipeline: :single_line, author: @commit.author)
- if @commit.description.present?
%pre.commit-description
= preserve(markdown(@commit.description, pipeline: :single_line, author: @commit.author))
+.commit-info-widget
+ .widget-row.branch-info
+ .icon-container.commit-icon
+ = custom_icon("icon_commit")
+ %span.cgray= pluralize(@commit.parents.count, "parent")
+ - @commit.parents.each do |parent|
+ = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
+ %span.commit-info.branches
+ %i.fa.fa-spinner.fa-spin
+
+ - if @commit.status
+ .widget-row.pipeline-info
+ .icon-container
+ = ci_icon_for_status(@commit.status)
+ Pipeline
+ = link_to "##{@commit.pipelines.last.id}", pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "monospace"
+ for
+ = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
+ %span.ci-status-label
+ = ci_label_for_status(@commit.status)
+ in
+ = time_interval_in_words @commit.pipelines.total_duration
+
:javascript
$(".commit-info.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}");
diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml
index f2d71fa6989..18daa2ee693 100644
--- a/app/views/projects/commit/_pipeline_status_group.html.haml
+++ b/app/views/projects/commit/_pipeline_status_group.html.haml
@@ -1,7 +1,7 @@
- group_status = CommitStatus.where(id: subject).status
%button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
%span.ci-status-icon
- = render_status_with_link('build', group_status)
+ = ci_icon_for_status(group_status)
%span.ci-status-text
= name
%span.badge= subject.size
diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml
index ac451441eec..2dc91a9b762 100644
--- a/app/views/projects/commit/_pipelines_list.haml
+++ b/app/views/projects/commit/_pipelines_list.haml
@@ -12,4 +12,4 @@
%th Stages
%th
%th
- = render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, status_icon_only: true, show_commit: false
+ = render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, show_commit: false
diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml
index f9d7eac3542..077b2d2725b 100644
--- a/app/views/projects/commit/builds.html.haml
+++ b/app/views/projects/commit/builds.html.haml
@@ -3,8 +3,7 @@
= render "projects/commits/head"
%div{ class: container_class }
- .prepend-top-default
- = render "commit_box"
+ = render "commit_box"
= render "ci_menu"
= render "builds"
diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml
index d85d6729a81..8233e26e4e7 100644
--- a/app/views/projects/commit/pipelines.html.haml
+++ b/app/views/projects/commit/pipelines.html.haml
@@ -1,7 +1,6 @@
- page_title "Pipelines", "#{@commit.title} (#{@commit.short_id})", "Commits"
-.prepend-top-default
- = render "commit_box"
+= render "commit_box"
= render "ci_menu"
= render "pipelines_list", pipelines: @ci_pipelines
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index cebf58d63df..b8c64d1f13e 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -4,8 +4,7 @@
= render "projects/commits/head"
%div{ class: container_class }
- .prepend-top-default
- = render "commit_box"
+ = render "commit_box"
- if @commit.status
= render "ci_menu"
- else
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index b647882efa0..247d612ba6f 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,5 +1,9 @@
- @no_container = true
- page_title "Cycle Analytics"
+
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('cycle_analytics/cycle_analytics_bundle.js')
+
= render "projects/pipelines/head"
#cycle-analytics{class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) }}
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 257e0a855bd..8f4f9ad4a80 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -8,7 +8,6 @@
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
= icon('comment')
\
- = clipboard_button(clipboard_text: diff_file.new_path, class: 'btn-file-option')
- if editable_diff?(diff_file)
- link_opts = @merge_request.id ? { from_merge_request_id: @merge_request.id } : {}
= edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml
index a6a2e5690b5..73993f35b39 100644
--- a/app/views/projects/diffs/_file_header.html.haml
+++ b/app/views/projects/diffs/_file_header.html.haml
@@ -21,6 +21,8 @@
- if diff_file.deleted_file
deleted
+ = clipboard_button(clipboard_text: diff_file.new_path, class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy filename to clipboard')
+
- if diff_file.mode_changed?
%small
= "#{diff_file.a_mode} → #{diff_file.b_mode}"
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 28aad3f4725..78aa9fb7391 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -1,5 +1,5 @@
/ Side-by-side diff view
-%div.text-file.diff-wrap-lines.code.file-content.js-syntax-highlight{ data: diff_view_data }
+%div.text-file.diff-wrap-lines.code.js-syntax-highlight{ data: diff_view_data }
%table
- last_line = 0
- diff_file.parallel_diff_lines.each do |line|
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 30473d14b9b..ff6a899b92a 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -118,7 +118,8 @@
Project avatar
.form-group
- if @project.avatar?
- = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
+ .image-container.s160
+ = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
%p.light
- if @project.avatar_in_git
Project avatar in repository: #{ @project.avatar_in_git }
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml
index c45b73e4225..1c457244a7a 100644
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml
+++ b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml
@@ -2,9 +2,9 @@
- if subject.target_url
= link_to subject.target_url do
%span.ci-status-icon
- = render_status_with_link('commit status', subject.status)
+ = ci_icon_for_status(subject.status)
%span.ci-status-text= subject.name
- else
%span.ci-status-icon
- = render_status_with_link('commit status', subject.status)
+ = ci_icon_for_status(subject.status)
%span.ci-status-text= subject.name
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index 44683c8bcdb..1892ebb512f 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -4,7 +4,7 @@
%ul.unstyled-list.related-merge-requests
- @related_branches.each do |branch|
%li
- - target = @project.repository.find_branch(branch).target
+ - target = @project.repository.find_branch(branch).dereferenced_target
- pipeline = @project.pipeline_for(branch, target.sha) if target
- if pipeline
%span.related-branch-ci-status
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index f135bf6f6b4..05a8475dcd6 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -22,14 +22,14 @@
%ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
%p.empty-message{ class: ('hidden' unless @prioritized_labels.empty?) } No prioritized labels yet
- if @prioritized_labels.present?
- = render partial: 'shared/label', collection: @prioritized_labels, as: :label
+ = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label
.other-labels
- if can?(current_user, :admin_label, @project)
%h5{ class: ('hide' if hide) } Other Labels
%ul.content-list.manage-labels-list.js-other-labels
- if @labels.present?
- = render partial: 'shared/label', collection: @labels, as: :label
+ = render partial: 'shared/label', subject: @project, collection: @labels, as: :label
= paginate @labels, theme: 'gitlab'
- if @labels.blank?
.nothing-here-block
diff --git a/app/views/projects/pipelines_settings/show.html.haml b/app/views/projects/pipelines_settings/show.html.haml
index bebf0ccd54d..96221a20502 100644
--- a/app/views/projects/pipelines_settings/show.html.haml
+++ b/app/views/projects/pipelines_settings/show.html.haml
@@ -5,33 +5,59 @@
%h4.prepend-top-0
= page_title
.col-lg-9
- %h5.prepend-top-0
- Pipelines
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f|
%fieldset.builds-feature
- unless @repository.gitlab_ci_yml
.form-group
%p Pipelines need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
+ %hr
+ .form-group.append-bottom-default
+ = f.label :runners_token, "Runner token", class: 'label-light'
+ = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
+ %p.help-block The secure token used by the Runner to checkout the project
+
+ %hr
.form-group
- %p Get recent application code using the following command:
+ %h5.prepend-top-0
+ Git strategy for pipelines
+ %p
+ Choose between <code>clone</code> or <code>fetch</code> to get the recent application code
+ = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy')
.radio
= f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false'
%strong git clone
%br
- %span.descr Slower but makes sure you have a clean dir before every build
+ %span.descr
+ Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job
.radio
= f.label :build_allow_git_fetch_true do
= f.radio_button :build_allow_git_fetch, 'true'
%strong git fetch
%br
- %span.descr Faster
+ %span.descr
+ Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)
+ %hr
.form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
- %p.help-block per build in minutes
+ %p.help-block
+ Per job in minutes. If a job passes this threshold, it will be marked as failed.
+ = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout')
+
+ %hr
+ .form-group
+ .checkbox
+ = f.label :public_builds do
+ = f.check_box :public_builds
+ %strong Public pipelines
+ .help-block
+ Allow everyone to access pipelines for public and internal projects
+ = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'visibility-of-pipelines')
+
+ %hr
.form-group
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group
@@ -39,8 +65,9 @@
= f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
%span.input-group-addon /
%p.help-block
- We will use this regular expression to find test coverage output in build trace.
- Leave blank if you want to disable this feature
+ A regular expression that will be used to find the test coverage
+ output in the build trace. Leave blank to disable
+ = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'test-coverage-parsing')
.bs-callout.bs-callout-info
%p Below are examples of regex for existing tools:
%ul
@@ -57,21 +84,9 @@
gcovr (C/C++) -
%code ^TOTAL.*\s+(\d+\%)$
%li
- tap --coverage-report=text-summary (Node.js) -
+ tap --coverage-report=text-summary (NodeJS) -
%code ^Statements\s*:\s*([^%]+)
- .form-group
- .checkbox
- = f.label :public_builds do
- = f.check_box :public_builds
- %strong Public builds
- .help-block Allow everyone to access builds traces for Public and Internal projects
-
- .form-group.append-bottom-default
- = f.label :runners_token, "Runners token", class: 'label-light'
- = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
- %p.help-block The secure token used to checkout project.
-
= f.submit 'Save changes', class: "btn btn-save"
%hr
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 05fccb4f976..c42641afea0 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -1,4 +1,4 @@
-- commit = @repository.commit(tag.target)
+- commit = @repository.commit(tag.dereferenced_target)
- release = @releases.find { |release| release.tag == tag.name }
%li
%div
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index 3480800369a..c367ae336db 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,6 +1,9 @@
%ul.nav-links.event-filter.scrolling-tabs
= event_filter_link EventFilter.all, 'All'
- = event_filter_link EventFilter.push, 'Push events'
- = event_filter_link EventFilter.merged, 'Merge events'
- = event_filter_link EventFilter.comments, 'Comments'
+ - if event_filter_visible(:repository)
+ = event_filter_link EventFilter.push, 'Push events'
+ - if event_filter_visible(:merge_requests)
+ = event_filter_link EventFilter.merged, 'Merge events'
+ - if event_filter_visible(:issues)
+ = event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 40c8d2af226..6ccdef0df46 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -1,6 +1,7 @@
- label_css_id = dom_id(label)
-- open_issues_count = label.open_issues_count(current_user, @project)
-- open_merge_requests_count = label.open_merge_requests_count(current_user, @project)
+- open_issues_count = label.open_issues_count(current_user)
+- open_merge_requests_count = label.open_merge_requests_count(current_user)
+- subject = local_assigns[:subject]
%li{id: label_css_id, data: { id: label.id } }
= render "shared/label_row", label: label
@@ -12,10 +13,10 @@
.dropdown-menu.dropdown-menu-align-right
%ul
%li
- = link_to_label(label, subject: @project, type: :merge_request) do
+ = link_to_label(label, subject: subject, type: :merge_request) do
= pluralize open_merge_requests_count, 'merge request'
%li
- = link_to_label(label, subject: @project) do
+ = link_to_label(label, subject: subject) do
= pluralize open_issues_count, 'open issue'
- if current_user
%li.label-subscription{ data: toggle_subscription_data(label) }
@@ -28,9 +29,9 @@
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, remote: true, data: {confirm: 'Remove this label? Are you sure?'}
.pull-right.hidden-xs.hidden-sm.hidden-md
- = link_to_label(label, subject: @project, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
+ = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
= pluralize open_merge_requests_count, 'merge request'
- = link_to_label(label, subject: @project, css_class: 'btn btn-transparent btn-action') do
+ = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
= pluralize open_issues_count, 'open issue'
- if current_user
diff --git a/app/views/shared/empty_states/_todos_all_done.svg b/app/views/shared/empty_states/_todos_all_done.svg
new file mode 100644
index 00000000000..94b5c2e0ea0
--- /dev/null
+++ b/app/views/shared/empty_states/_todos_all_done.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 293 216"><g fill="none" fill-rule="evenodd"><g transform="rotate(-5 211.388 -693.89)"><rect width="163.6" height="200" x=".2" fill="#FFF" stroke="#EEE" stroke-width="3" stroke-linecap="round" stroke-dasharray="6 9" rx="6"/><g transform="translate(24 38)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#6B4FBB" opacity=".5" rx="1.5"/></g><g transform="translate(24 83)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g><g transform="translate(24 130)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g></g><path fill="#FFCE29" d="M30 11l-1.8 4-2-4-4-1.8 4-2 2-4 2 4 4 2M286 60l-2.7 6.3-3-6-6-3 6-3 3-6 2.8 6.2 6.6 2.8M263 97l-2 4-2-4-4-2 4-2 2-4 2 4 4 2M12 85l-2.7 6.3-3-6-6-3 6-3 3-6 2.8 6.2 6.6 2.8"/></g></svg>
diff --git a/app/views/shared/empty_states/_todos_empty.svg b/app/views/shared/empty_states/_todos_empty.svg
new file mode 100644
index 00000000000..b1e661268fb
--- /dev/null
+++ b/app/views/shared/empty_states/_todos_empty.svg
@@ -0,0 +1,110 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 284 337" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <rect id="a" width="180" height="220" x="66.2" y="74.4" rx="6"/>
+ <mask id="l" width="180" height="220" x="0" y="0" fill="#fff">
+ <use xlink:href="#a"/>
+ </mask>
+ <rect id="b" width="180" height="220" rx="6"/>
+ <mask id="m" width="180" height="220" x="0" y="0" fill="#fff">
+ <use xlink:href="#b"/>
+ </mask>
+ <rect id="c" width="28" height="28" rx="4"/>
+ <mask id="n" width="28" height="28" x="0" y="0" fill="#fff">
+ <use xlink:href="#c"/>
+ </mask>
+ <rect id="d" width="28" height="28" rx="4"/>
+ <mask id="o" width="28" height="28" x="0" y="0" fill="#fff">
+ <use xlink:href="#d"/>
+ </mask>
+ <circle id="e" cx="21.5" cy="21.5" r="21.5"/>
+ <mask id="p" width="43" height="43" x="0" y="0" fill="#fff">
+ <use xlink:href="#e"/>
+ </mask>
+ <circle id="f" cx="26.5" cy="26.5" r="26.5"/>
+ <mask id="q" width="53" height="53" x="0" y="0" fill="#fff">
+ <use xlink:href="#f"/>
+ </mask>
+ <circle id="g" cx="9.5" cy="4.5" r="4.5"/>
+ <mask id="r" width="13" height="13" x="-2" y="-2">
+ <path fill="#fff" d="M3-2h13v13H3z"/>
+ <use xlink:href="#g"/>
+ </mask>
+ <circle id="h" cx="26.5" cy="26.5" r="26.5"/>
+ <mask id="s" width="53" height="53" x="0" y="0" fill="#fff">
+ <use xlink:href="#h"/>
+ </mask>
+ <circle id="i" cx="21.5" cy="21.5" r="21.5"/>
+ <mask id="t" width="43" height="43" x="0" y="0" fill="#fff">
+ <use xlink:href="#i"/>
+ </mask>
+ <path id="j" d="M18 38h15c10.5 0 19-8.5 19-19S43.5 0 33 0H19C8.5 0 0 8.5 0 19c0 6.3 3 12 7.8 15.3l5.2 9c.6 1 1.4 1 2 0l3-5.3z"/>
+ <mask id="u" width="52" height="44" x="0" y="0" fill="#fff">
+ <use xlink:href="#j"/>
+ </mask>
+ <circle id="k" cx="18.5" cy="18.5" r="18.5"/>
+ <mask id="v" width="37" height="37" x="0" y="0" fill="#fff">
+ <use xlink:href="#k"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd" transform="translate(-6 -4)">
+ <use stroke="#EEE" stroke-width="6" mask="url(#l)" transform="rotate(-5 156.245 184.425)" xlink:href="#a"/>
+ <g transform="rotate(5 -707.333 618.042)">
+ <use fill="#FFF" stroke="#EEE" stroke-width="6" mask="url(#m)" xlink:href="#b"/>
+ <g transform="translate(29 24)">
+ <path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/>
+ <path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/>
+ <rect width="86" height="3" x="40" y="11" fill="#6B4FBB" opacity=".5" rx="1.5"/>
+ <rect width="43" height="3" x="40" y="21" fill="#6B4FBB" opacity=".5" rx="1.5"/>
+ </g>
+ <g transform="translate(29 69)">
+ <path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/>
+ <path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/>
+ <rect width="86" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/>
+ <rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/>
+ </g>
+ <g transform="translate(28 160)">
+ <use stroke="#E5E5E5" stroke-width="6" mask="url(#n)" opacity=".7" xlink:href="#c"/>
+ <rect width="26" height="3" x="41" y="7" fill="#ECECEC" rx="1.5"/>
+ <rect width="43" height="3" x="41" y="17" fill="#ECECEC" rx="1.5"/>
+ </g>
+ <g transform="translate(28 116)">
+ <use stroke="#E5E5E5" stroke-width="6" mask="url(#o)" xlink:href="#d"/>
+ <rect width="86" height="3" x="41" y="7" fill="#E5E5E5" rx="1.5"/>
+ <rect width="43" height="3" x="41" y="17" fill="#E5E5E5" rx="1.5"/>
+ </g>
+ </g>
+ <g transform="rotate(-15 601.917 -782.362)">
+ <use fill="#FFF" stroke="#B5A7DD" stroke-width="6" mask="url(#p)" xlink:href="#e"/>
+ <text fill="#6B4FBB" font-family="SourceSansPro-Black, Source Sans Pro" font-size="20" font-weight="700" letter-spacing="-.1">
+ <tspan x="12" y="27">@</tspan>
+ </text>
+ </g>
+ <g transform="rotate(15 -686.59 1035.907)">
+ <use fill="#FFF" stroke="#FDE5D8" stroke-width="6" mask="url(#q)" xlink:href="#f"/>
+ <path fill="#FC6D26" d="M26.5 38.2c3.3 0 9.5-2.5 9.5-9.6 0-7-2.4-6.6-9.5-6.6-7 0-9.5-.4-9.5 6.6s6.2 9.6 9.5 9.6z"/>
+ <g transform="translate(17 14)">
+ <use fill="#FC6D26" xlink:href="#g"/>
+ <use stroke="#FFF" stroke-width="4" mask="url(#r)" xlink:href="#g"/>
+ </g>
+ </g>
+ <g transform="rotate(15 -85.125 65.185)">
+ <use fill="#FFF" stroke="#B5A7DD" stroke-width="6" mask="url(#s)" xlink:href="#h"/>
+ <path fill="#6B4FBB" d="M24 18.5c0-1.4 1-2.5 2.5-2.5 1.4 0 2.5 1 2.5 2.5v9c0 1.4-1 2.5-2.5 2.5-1.4 0-2.5-1-2.5-2.5v-9zM26.5 37c1.4 0 2.5-1 2.5-2.5 0-1.4-1-2.5-2.5-2.5-1.4 0-2.5 1-2.5 2.5 0 1.4 1 2.5 2.5 2.5z"/>
+ </g>
+ <g transform="rotate(-15 716.492 78.873)">
+ <use fill="#FFF" stroke="#FDE5D8" stroke-width="6" mask="url(#t)" xlink:href="#i"/>
+ <path fill="#FC6D26" d="M20 23v-3h3v3h-3zm0 3v1.5c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5V26h-2.5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5H17v-3h-1.5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5H17v-2.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V17h3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V17h2.5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5H26v3h1.5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5H26v2.5c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5V26h-3z"/>
+ </g>
+ <g transform="rotate(-15 129.114 -585.74)">
+ <use stroke="#FDE5D8" stroke-width="6" mask="url(#u)" xlink:href="#j"/>
+ <circle cx="16" cy="20" r="2" fill="#FC6D26"/>
+ <circle cx="27" cy="20" r="2" fill="#FC6D26"/>
+ <circle cx="38" cy="20" r="2" fill="#FC6D26"/>
+ </g>
+ <g transform="rotate(-15 1254.8 -458.986)">
+ <use stroke="#FDE5D8" stroke-width="6" mask="url(#v)" xlink:href="#k"/>
+ <path fill="#FC6D26" d="M10.6 19l2-2c.5-.5.5-1 0-1.5-.3-.4-1-.4-1.3 0l-2.8 2.8c-.2.2-.3.4-.3.7 0 .3 0 .5.3.7l2.8 2.8c.4.4 1 .4 1.4 0 .4-.4.4-1 0-1.4l-2-2zm14.8 0l-2-2c-.5-.5-.5-1 0-1.5.3-.4 1-.4 1.3 0l2.8 2.8c.2.2.3.4.3.7 0 .3 0 .5-.3.7l-2.8 2.8c-.4.4-1 .4-1.4 0-.4-.4-.4-1 0-1.4l2-2z"/>
+ <rect width="2" height="7" x="17" y="15.1" fill="#FC6D26" opacity=".5" transform="rotate(15 18.002 18.64)" rx="1"/>
+ </g>
+ </g>
+</svg>
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index dc4ee3074d2..562291a61df 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -24,7 +24,8 @@
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
= visibility_level_icon(group.visibility_level, fw: false)
- = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+ .image-container.s40
+ = image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title
= link_to group, class: 'group-name' do
= group.name
diff --git a/app/views/shared/icons/_icon_close.svg b/app/views/shared/icons/_icon_close.svg
new file mode 100644
index 00000000000..9d62012518b
--- /dev/null
+++ b/app/views/shared/icons/_icon_close.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path d="M9,7.5l5.83-5.91a.48.48,0,0,0,0-.69L14.11.15a.46.46,0,0,0-.68,0l-5.93,6L1.57.15a.46.46,0,0,0-.68,0L.15.9a.48.48,0,0,0,0,.69L6,7.5.15,13.41a.48.48,0,0,0,0,.69l.74.75a.46.46,0,0,0,.68,0l5.93-6,5.93,6a.46.46,0,0,0,.68,0l.74-.75a.48.48,0,0,0,0-.69Z"/></svg> \ No newline at end of file
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 3c03c220ddd..9e1b0379428 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -3,8 +3,9 @@
- assignee = issuable.assignee
- issuable_type = issuable.class.table_name
- base_url_args = [project.namespace.becomes(Namespace), project, issuable_type]
+- can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable)
-%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) }
+%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'ui-sort-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) }
%span
- if show_project_name
%strong #{project.name} &middot;
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index e8668048703..3d2122a159c 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -32,10 +32,11 @@
= link_to project_path(project), class: dom_class(project) do
- if avatar
.dash-project-avatar
- - if use_creator_avatar
- = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
- - else
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+ .image-container.s40
+ - if use_creator_avatar
+ = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
+ - else
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40')
%span.project-full-name
%span.namespace-name
- if project.namespace && !skip_namespace
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index f360fbb3d5d..78f253f9023 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,4 +1,5 @@
.clearfix
- groups.each do |group|
= link_to group, class: 'profile-groups-avatars inline', title: group.name do
- = image_tag group_icon(group), class: 'avatar group-avatar s40'
+ .image-container.s40
+ = image_tag group_icon(group), class: 'avatar group-avatar s40'
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index 71b274e0c99..4dfa745fb50 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -9,6 +9,18 @@ class ProjectCacheWorker
LEASE_TIMEOUT = 15.minutes.to_i
+ def self.lease_for(project_id)
+ Gitlab::ExclusiveLease.
+ new("project_cache_worker:#{project_id}", timeout: LEASE_TIMEOUT)
+ end
+
+ # Overwrite Sidekiq's implementation so we only schedule when actually needed.
+ def self.perform_async(project_id)
+ # If a lease for this project is still being held there's no point in
+ # scheduling a new job.
+ super unless lease_for(project_id).exists?
+ end
+
def perform(project_id)
if try_obtain_lease_for(project_id)
Rails.logger.
@@ -37,8 +49,6 @@ class ProjectCacheWorker
end
def try_obtain_lease_for(project_id)
- Gitlab::ExclusiveLease.
- new("project_cache_worker:#{project_id}", timeout: LEASE_TIMEOUT).
- try_obtain
+ self.class.lease_for(project_id).try_obtain
end
end
diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb
index efb85eafd15..d973e662ff2 100644
--- a/app/workers/project_web_hook_worker.rb
+++ b/app/workers/project_web_hook_worker.rb
@@ -2,6 +2,8 @@ class ProjectWebHookWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ sidekiq_options retry: 4
+
def perform(hook_id, data, hook_name)
data = data.with_indifferent_access
WebHook.find(hook_id).execute(data, hook_name)
diff --git a/app/workers/remove_unreferenced_lfs_objects_worker.rb b/app/workers/remove_unreferenced_lfs_objects_worker.rb
new file mode 100644
index 00000000000..b80f131d5f7
--- /dev/null
+++ b/app/workers/remove_unreferenced_lfs_objects_worker.rb
@@ -0,0 +1,8 @@
+class RemoveUnreferencedLfsObjectsWorker
+ include Sidekiq::Worker
+ include CronjobQueue
+
+ def perform
+ LfsObject.destroy_unreferenced
+ end
+end